Beging IO stream out from allocation to surface texture.
Change-Id: I4d6b7f7740a896d39b811d6fe7532bb00db62373
This commit is contained in:
@@ -102,7 +102,7 @@ public class Allocation extends BaseObj {
|
||||
public static final int USAGE_SCRIPT = 0x0001;
|
||||
|
||||
/**
|
||||
* GRAPHICS_TEXTURE The allcation will be used as a texture
|
||||
* GRAPHICS_TEXTURE The allocation will be used as a texture
|
||||
* source by one or more graphics programs.
|
||||
*
|
||||
*/
|
||||
@@ -124,34 +124,34 @@ public class Allocation extends BaseObj {
|
||||
public static final int USAGE_GRAPHICS_CONSTANTS = 0x0008;
|
||||
|
||||
/**
|
||||
* USAGE_GRAPHICS_RENDER_TARGET The allcation will be used as a
|
||||
* USAGE_GRAPHICS_RENDER_TARGET The allocation will be used as a
|
||||
* target for offscreen rendering
|
||||
*
|
||||
*/
|
||||
public static final int USAGE_GRAPHICS_RENDER_TARGET = 0x0010;
|
||||
|
||||
/**
|
||||
* USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT The allocation will be
|
||||
* used with a SurfaceTexture object. This usage will cause the
|
||||
* allocation to be created read only.
|
||||
* USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE The allocation
|
||||
* will be used as a SurfaceTexture graphics consumer. This
|
||||
* usage may only be used with USAGE_GRAPHICS_TEXTURE.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE = 0x0020;
|
||||
|
||||
/**
|
||||
* USAGE_IO_INPUT The allocation will be
|
||||
* used with a SurfaceTexture object. This usage will cause the
|
||||
* allocation to be created read only.
|
||||
* USAGE_IO_INPUT The allocation will be used as SurfaceTexture
|
||||
* consumer. This usage will cause the allocation to be created
|
||||
* read only.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int USAGE_IO_INPUT = 0x0040;
|
||||
|
||||
/**
|
||||
* USAGE_IO_OUTPUT The allocation will be
|
||||
* used with a SurfaceTexture object. This usage will cause the
|
||||
* allocation to be created write only.
|
||||
* USAGE_IO_OUTPUT The allocation will be used as a
|
||||
* SurfaceTexture producer. The dimensions and format of the
|
||||
* SurfaceTexture will be forced to those of the allocation.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@@ -323,6 +323,37 @@ public class Allocation extends BaseObj {
|
||||
mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a buffer to the output stream. The contents of the
|
||||
* Allocation will be undefined after this operation.
|
||||
*
|
||||
* @hide
|
||||
*
|
||||
*/
|
||||
public void ioSendOutput() {
|
||||
if ((mUsage & USAGE_IO_OUTPUT) == 0) {
|
||||
throw new RSIllegalArgumentException(
|
||||
"Can only send buffer if IO_OUTPUT usage specified.");
|
||||
}
|
||||
mRS.validate();
|
||||
mRS.nAllocationIoSend(getID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive the latest input into the Allocation.
|
||||
*
|
||||
* @hide
|
||||
*
|
||||
*/
|
||||
public void ioGetInput() {
|
||||
if ((mUsage & USAGE_IO_INPUT) == 0) {
|
||||
throw new RSIllegalArgumentException(
|
||||
"Can only send buffer if IO_OUTPUT usage specified.");
|
||||
}
|
||||
mRS.validate();
|
||||
mRS.nAllocationIoReceive(getID());
|
||||
}
|
||||
|
||||
public void copyFrom(BaseObj[] d) {
|
||||
mRS.validate();
|
||||
validateIsObject();
|
||||
@@ -887,17 +918,37 @@ public class Allocation extends BaseObj {
|
||||
updateCacheInfo(mType);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Resize a 2D allocation. The contents of the allocation are
|
||||
* preserved. If new elements are allocated objects are created
|
||||
* with null contents and the new region is otherwise undefined.
|
||||
*
|
||||
* If the new region is smaller the references of any objects
|
||||
* outside the new region will be released.
|
||||
*
|
||||
* A new type will be created with the new dimension.
|
||||
*
|
||||
* @hide
|
||||
* @param dimX The new size of the allocation.
|
||||
* @param dimY The new size of the allocation.
|
||||
*/
|
||||
public void resize(int dimX, int dimY) {
|
||||
if ((mType.getZ() > 0) || mType.getFaces() || mType.getLOD()) {
|
||||
throw new RSIllegalStateException("Resize only support for 2D allocations at this time.");
|
||||
if ((mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
|
||||
throw new RSInvalidStateException(
|
||||
"Resize only support for 2D allocations at this time.");
|
||||
}
|
||||
if (mType.getY() == 0) {
|
||||
throw new RSIllegalStateException("Resize only support for 2D allocations at this time.");
|
||||
throw new RSInvalidStateException(
|
||||
"Resize only support for 2D allocations at this time.");
|
||||
}
|
||||
mRS.nAllocationResize2D(getID(), dimX, dimY);
|
||||
mRS.finish(); // Necessary because resize is fifoed and update is async.
|
||||
|
||||
int typeID = mRS.nAllocationGetType(getID());
|
||||
mType = new Type(typeID, mRS);
|
||||
mType.updateFromNative();
|
||||
updateCacheInfo(mType);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -1090,6 +1141,18 @@ public class Allocation extends BaseObj {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setSurfaceTexture(SurfaceTexture sur) {
|
||||
if ((mUsage & USAGE_IO_OUTPUT) == 0) {
|
||||
throw new RSInvalidStateException("Allocation is not USAGE_IO_OUTPUT.");
|
||||
}
|
||||
|
||||
mRS.validate();
|
||||
mRS.nAllocationSetSurfaceTexture(getID(), sur);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a non-mipmapped renderscript allocation to use as a
|
||||
|
||||
@@ -274,6 +274,22 @@ public class RenderScript {
|
||||
validate();
|
||||
return rsnAllocationGetSurfaceTextureID(mContext, alloc);
|
||||
}
|
||||
native void rsnAllocationSetSurfaceTexture(int con, int alloc, SurfaceTexture sur);
|
||||
synchronized void nAllocationSetSurfaceTexture(int alloc, SurfaceTexture sur) {
|
||||
validate();
|
||||
rsnAllocationSetSurfaceTexture(mContext, alloc, sur);
|
||||
}
|
||||
native void rsnAllocationIoSend(int con, int alloc);
|
||||
synchronized void nAllocationIoSend(int alloc) {
|
||||
validate();
|
||||
rsnAllocationIoSend(mContext, alloc);
|
||||
}
|
||||
native void rsnAllocationIoReceive(int con, int alloc);
|
||||
synchronized void nAllocationIoReceive(int alloc) {
|
||||
validate();
|
||||
rsnAllocationIoReceive(mContext, alloc);
|
||||
}
|
||||
|
||||
|
||||
native void rsnAllocationGenerateMipmaps(int con, int alloc);
|
||||
synchronized void nAllocationGenerateMipmaps(int alloc) {
|
||||
|
||||
@@ -450,6 +450,37 @@ nAllocationGetSurfaceTextureID(JNIEnv *_env, jobject _this, RsContext con, jint
|
||||
return rsAllocationGetSurfaceTextureID(con, (RsAllocation)a);
|
||||
}
|
||||
|
||||
static void
|
||||
nAllocationSetSurfaceTexture(JNIEnv *_env, jobject _this, RsContext con,
|
||||
RsAllocation alloc, jobject sur)
|
||||
{
|
||||
LOG_API("nAllocationSetSurfaceTexture, con(%p), alloc(%p), surface(%p)",
|
||||
con, alloc, (Surface *)sur);
|
||||
|
||||
sp<ANativeWindow> window;
|
||||
if (sur != 0) {
|
||||
sp<SurfaceTexture> st = SurfaceTexture_getSurfaceTexture(_env, sur);
|
||||
window = new SurfaceTextureClient(st);
|
||||
}
|
||||
|
||||
rsAllocationSetSurface(con, alloc, window.get());
|
||||
}
|
||||
|
||||
static void
|
||||
nAllocationIoSend(JNIEnv *_env, jobject _this, RsContext con, RsAllocation alloc)
|
||||
{
|
||||
LOG_API("nAllocationIoSend, con(%p), alloc(%p)", con, alloc);
|
||||
rsAllocationIoSend(con, alloc);
|
||||
}
|
||||
|
||||
static void
|
||||
nAllocationIoReceive(JNIEnv *_env, jobject _this, RsContext con, RsAllocation alloc)
|
||||
{
|
||||
LOG_API("nAllocationIoReceive, con(%p), alloc(%p)", con, alloc);
|
||||
rsAllocationIoReceive(con, alloc);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nAllocationGenerateMipmaps(JNIEnv *_env, jobject _this, RsContext con, jint alloc)
|
||||
{
|
||||
@@ -1277,6 +1308,9 @@ static JNINativeMethod methods[] = {
|
||||
|
||||
{"rsnAllocationSyncAll", "(III)V", (void*)nAllocationSyncAll },
|
||||
{"rsnAllocationGetSurfaceTextureID", "(II)I", (void*)nAllocationGetSurfaceTextureID },
|
||||
{"rsnAllocationSetSurfaceTexture", "(IILandroid/graphics/SurfaceTexture;)V", (void*)nAllocationSetSurfaceTexture },
|
||||
{"rsnAllocationIoSend", "(II)V", (void*)nAllocationIoSend },
|
||||
{"rsnAllocationIoReceive", "(II)V", (void*)nAllocationIoReceive },
|
||||
{"rsnAllocationData1D", "(IIIII[II)V", (void*)nAllocationData1D_i },
|
||||
{"rsnAllocationData1D", "(IIIII[SI)V", (void*)nAllocationData1D_s },
|
||||
{"rsnAllocationData1D", "(IIIII[BI)V", (void*)nAllocationData1D_b },
|
||||
|
||||
@@ -23,6 +23,11 @@
|
||||
|
||||
#include "rsAllocation.h"
|
||||
|
||||
#include "system/window.h"
|
||||
#include "hardware/gralloc.h"
|
||||
#include "ui/Rect.h"
|
||||
#include "ui/GraphicBufferMapper.h"
|
||||
|
||||
#include <GLES/gl.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES/glext.h>
|
||||
@@ -220,7 +225,8 @@ bool rsdAllocationInit(const Context *rsc, Allocation *alloc, bool forceZero) {
|
||||
}
|
||||
|
||||
void * ptr = alloc->mHal.state.usrPtr;
|
||||
if (!ptr) {
|
||||
if (alloc->mHal.state.usageFlags & RS_ALLOCATION_USAGE_IO_OUTPUT) {
|
||||
} else {
|
||||
ptr = malloc(alloc->mHal.state.type->getSizeBytes());
|
||||
if (!ptr) {
|
||||
free(drv);
|
||||
@@ -248,7 +254,7 @@ bool rsdAllocationInit(const Context *rsc, Allocation *alloc, bool forceZero) {
|
||||
alloc->mHal.drvState.mallocPtr = ptr;
|
||||
drv->mallocPtr = (uint8_t *)ptr;
|
||||
alloc->mHal.drv = drv;
|
||||
if (forceZero) {
|
||||
if (forceZero && ptr) {
|
||||
memset(ptr, 0, alloc->mHal.state.type->getSizeBytes());
|
||||
}
|
||||
|
||||
@@ -386,6 +392,93 @@ int32_t rsdAllocationInitSurfaceTexture(const Context *rsc, const Allocation *al
|
||||
return drv->textureID;
|
||||
}
|
||||
|
||||
static bool IoGetBuffer(const Context *rsc, Allocation *alloc, ANativeWindow *nw) {
|
||||
DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
|
||||
|
||||
int32_t r = nw->dequeueBuffer(nw, &drv->wndBuffer);
|
||||
if (r) {
|
||||
rsc->setError(RS_ERROR_DRIVER, "Error getting next IO output buffer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// This lock is implicitly released by the queue buffer in IoSend
|
||||
r = nw->lockBuffer(nw, drv->wndBuffer);
|
||||
if (r) {
|
||||
rsc->setError(RS_ERROR_DRIVER, "Error locking next IO output buffer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must lock the whole surface
|
||||
GraphicBufferMapper &mapper = GraphicBufferMapper::get();
|
||||
Rect bounds(drv->wndBuffer->width, drv->wndBuffer->height);
|
||||
|
||||
void *dst = NULL;
|
||||
mapper.lock(drv->wndBuffer->handle,
|
||||
GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN,
|
||||
bounds, &dst);
|
||||
alloc->mHal.drvState.mallocPtr = dst;
|
||||
return true;
|
||||
}
|
||||
|
||||
void rsdAllocationSetSurfaceTexture(const Context *rsc, Allocation *alloc, ANativeWindow *nw) {
|
||||
DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
|
||||
|
||||
//ALOGE("rsdAllocationSetSurfaceTexture %p %p", alloc, nw);
|
||||
|
||||
// Cleanup old surface if there is one.
|
||||
if (alloc->mHal.state.wndSurface) {
|
||||
ANativeWindow *old = alloc->mHal.state.wndSurface;
|
||||
GraphicBufferMapper &mapper = GraphicBufferMapper::get();
|
||||
mapper.unlock(drv->wndBuffer->handle);
|
||||
old->queueBuffer(old, drv->wndBuffer);
|
||||
}
|
||||
|
||||
if (nw != NULL) {
|
||||
int32_t r;
|
||||
r = native_window_set_usage(nw, GRALLOC_USAGE_SW_READ_RARELY |
|
||||
GRALLOC_USAGE_SW_WRITE_OFTEN);
|
||||
if (r) {
|
||||
rsc->setError(RS_ERROR_DRIVER, "Error setting IO output buffer usage.");
|
||||
return;
|
||||
}
|
||||
|
||||
r = native_window_set_buffers_dimensions(nw, alloc->mHal.state.dimensionX,
|
||||
alloc->mHal.state.dimensionY);
|
||||
if (r) {
|
||||
rsc->setError(RS_ERROR_DRIVER, "Error setting IO output buffer dimensions.");
|
||||
return;
|
||||
}
|
||||
|
||||
r = native_window_set_buffer_count(nw, 3);
|
||||
if (r) {
|
||||
rsc->setError(RS_ERROR_DRIVER, "Error setting IO output buffer count.");
|
||||
return;
|
||||
}
|
||||
|
||||
IoGetBuffer(rsc, alloc, nw);
|
||||
}
|
||||
}
|
||||
|
||||
void rsdAllocationIoSend(const Context *rsc, Allocation *alloc) {
|
||||
DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv;
|
||||
ANativeWindow *nw = alloc->mHal.state.wndSurface;
|
||||
|
||||
GraphicBufferMapper &mapper = GraphicBufferMapper::get();
|
||||
mapper.unlock(drv->wndBuffer->handle);
|
||||
int32_t r = nw->queueBuffer(nw, drv->wndBuffer);
|
||||
if (r) {
|
||||
rsc->setError(RS_ERROR_DRIVER, "Error sending IO output buffer.");
|
||||
return;
|
||||
}
|
||||
|
||||
IoGetBuffer(rsc, alloc, nw);
|
||||
}
|
||||
|
||||
void rsdAllocationIoReceive(const Context *rsc, Allocation *alloc) {
|
||||
ALOGE("not implemented");
|
||||
}
|
||||
|
||||
|
||||
void rsdAllocationData1D(const Context *rsc, const Allocation *alloc,
|
||||
uint32_t xoff, uint32_t lod, uint32_t count,
|
||||
const void *data, uint32_t sizeBytes) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <GLES2/gl2.h>
|
||||
|
||||
class RsdFrameBufferObj;
|
||||
struct ANativeWindowBuffer;
|
||||
|
||||
struct DrvAllocation {
|
||||
// Is this a legal structure to be used as a texture source.
|
||||
@@ -47,6 +48,7 @@ struct DrvAllocation {
|
||||
bool uploadDeferred;
|
||||
|
||||
RsdFrameBufferObj * readBackFBO;
|
||||
ANativeWindowBuffer *wndBuffer;
|
||||
};
|
||||
|
||||
GLenum rsdTypeToGLType(RsDataType t);
|
||||
@@ -69,6 +71,12 @@ void rsdAllocationMarkDirty(const android::renderscript::Context *rsc,
|
||||
const android::renderscript::Allocation *alloc);
|
||||
int32_t rsdAllocationInitSurfaceTexture(const android::renderscript::Context *rsc,
|
||||
const android::renderscript::Allocation *alloc);
|
||||
void rsdAllocationSetSurfaceTexture(const android::renderscript::Context *rsc,
|
||||
android::renderscript::Allocation *alloc, ANativeWindow *nw);
|
||||
void rsdAllocationIoSend(const android::renderscript::Context *rsc,
|
||||
android::renderscript::Allocation *alloc);
|
||||
void rsdAllocationIoReceive(const android::renderscript::Context *rsc,
|
||||
android::renderscript::Allocation *alloc);
|
||||
|
||||
void rsdAllocationData1D(const android::renderscript::Context *rsc,
|
||||
const android::renderscript::Allocation *alloc,
|
||||
|
||||
@@ -74,6 +74,9 @@ static RsdHalFunctions FunctionTable = {
|
||||
rsdAllocationSyncAll,
|
||||
rsdAllocationMarkDirty,
|
||||
rsdAllocationInitSurfaceTexture,
|
||||
rsdAllocationSetSurfaceTexture,
|
||||
rsdAllocationIoSend,
|
||||
rsdAllocationIoReceive,
|
||||
rsdAllocationData1D,
|
||||
rsdAllocationData2D,
|
||||
rsdAllocationData3D,
|
||||
|
||||
@@ -69,6 +69,21 @@ AllocationGetSurfaceTextureID {
|
||||
ret int32_t
|
||||
}
|
||||
|
||||
AllocationSetSurface {
|
||||
param RsAllocation alloc
|
||||
param RsNativeWindow sur
|
||||
sync
|
||||
}
|
||||
|
||||
AllocationIoSend {
|
||||
param RsAllocation alloc
|
||||
}
|
||||
|
||||
AllocationIoReceive {
|
||||
param RsAllocation alloc
|
||||
}
|
||||
|
||||
|
||||
ContextFinish {
|
||||
sync
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "rsContext.h"
|
||||
#include "rs_hal.h"
|
||||
|
||||
#include "system/window.h"
|
||||
|
||||
using namespace android;
|
||||
using namespace android::renderscript;
|
||||
@@ -44,6 +45,7 @@ Allocation * Allocation::createAllocation(Context *rsc, const Type *type, uint32
|
||||
delete a;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
@@ -420,6 +422,28 @@ int32_t Allocation::getSurfaceTextureID(const Context *rsc) {
|
||||
return id;
|
||||
}
|
||||
|
||||
void Allocation::setSurface(const Context *rsc, RsNativeWindow sur) {
|
||||
ANativeWindow *nw = (ANativeWindow *)sur;
|
||||
ANativeWindow *old = mHal.state.wndSurface;
|
||||
if (nw) {
|
||||
nw->incStrong(NULL);
|
||||
}
|
||||
rsc->mHal.funcs.allocation.setSurfaceTexture(rsc, this, nw);
|
||||
mHal.state.wndSurface = nw;
|
||||
if (old) {
|
||||
old->decStrong(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void Allocation::ioSend(const Context *rsc) {
|
||||
rsc->mHal.funcs.allocation.ioSend(rsc, this);
|
||||
}
|
||||
|
||||
void Allocation::ioReceive(const Context *rsc) {
|
||||
rsc->mHal.funcs.allocation.ioReceive(rsc, this);
|
||||
}
|
||||
|
||||
|
||||
/////////////////
|
||||
//
|
||||
|
||||
@@ -670,6 +694,21 @@ int32_t rsi_AllocationGetSurfaceTextureID(Context *rsc, RsAllocation valloc) {
|
||||
return alloc->getSurfaceTextureID(rsc);
|
||||
}
|
||||
|
||||
void rsi_AllocationSetSurface(Context *rsc, RsAllocation valloc, RsNativeWindow sur) {
|
||||
Allocation *alloc = static_cast<Allocation *>(valloc);
|
||||
alloc->setSurface(rsc, sur);
|
||||
}
|
||||
|
||||
void rsi_AllocationIoSend(Context *rsc, RsAllocation valloc) {
|
||||
Allocation *alloc = static_cast<Allocation *>(valloc);
|
||||
alloc->ioSend(rsc);
|
||||
}
|
||||
|
||||
void rsi_AllocationIoReceive(Context *rsc, RsAllocation valloc) {
|
||||
Allocation *alloc = static_cast<Allocation *>(valloc);
|
||||
alloc->ioReceive(rsc);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "rsType.h"
|
||||
|
||||
struct ANativeWindow;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
namespace android {
|
||||
namespace renderscript {
|
||||
@@ -57,6 +59,7 @@ public:
|
||||
bool hasReferences;
|
||||
void * usrPtr;
|
||||
int32_t surfaceTextureID;
|
||||
ANativeWindow *wndSurface;
|
||||
};
|
||||
State state;
|
||||
|
||||
@@ -127,6 +130,9 @@ public:
|
||||
}
|
||||
|
||||
int32_t getSurfaceTextureID(const Context *rsc);
|
||||
void setSurface(const Context *rsc, RsNativeWindow sur);
|
||||
void ioSend(const Context *rsc);
|
||||
void ioReceive(const Context *rsc);
|
||||
|
||||
protected:
|
||||
Vector<const Program *> mToDirtyList;
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <RenderScriptDefines.h>
|
||||
|
||||
struct ANativeWindow;
|
||||
|
||||
namespace android {
|
||||
namespace renderscript {
|
||||
|
||||
@@ -115,7 +117,11 @@ typedef struct {
|
||||
bool zeroNew);
|
||||
void (*syncAll)(const Context *rsc, const Allocation *alloc, RsAllocationUsageType src);
|
||||
void (*markDirty)(const Context *rsc, const Allocation *alloc);
|
||||
|
||||
int32_t (*initSurfaceTexture)(const Context *rsc, const Allocation *alloc);
|
||||
void (*setSurfaceTexture)(const Context *rsc, Allocation *alloc, ANativeWindow *sur);
|
||||
void (*ioSend)(const Context *rsc, Allocation *alloc);
|
||||
void (*ioReceive)(const Context *rsc, Allocation *alloc);
|
||||
|
||||
void (*data1D)(const Context *rsc, const Allocation *alloc,
|
||||
uint32_t xoff, uint32_t lod, uint32_t count,
|
||||
|
||||
Reference in New Issue
Block a user