📄 surface.c
字号:
}
glReadPixels(rect->left, rect->top,
rect->right - rect->left,
rect->bottom - rect->top,
fmt, type, mem);
vcheckGLcall("glReadPixels");
/* TODO: Merge this with the palettization loop below for P8 targets */
if(!srcUpsideDown) {
UINT len, off;
/* glReadPixels returns the image upside down, and there is no way to prevent this.
Flip the lines in software */
len = (rect->right - rect->left) * bpp;
off = rect->left * bpp;
row = HeapAlloc(GetProcessHeap(), 0, len);
if(!row) {
ERR("Out of memory\n");
if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
return;
}
top = mem + pitch * rect->top;
bottom = ((BYTE *) mem) + pitch * ( rect->bottom - rect->top - 1);
for(i = 0; i < (rect->bottom - rect->top) / 2; i++) {
memcpy(row, top + off, len);
memcpy(top + off, bottom + off, len);
memcpy(bottom + off, row, len);
top += pitch;
bottom -= pitch;
}
HeapFree(GetProcessHeap(), 0, row);
}
if(This->resource.format == WINED3DFMT_P8) {
PALETTEENTRY *pal;
DWORD width = pitch / 3;
int x, y, c;
if(This->palette) {
pal = This->palette->palents;
} else {
pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
}
for(y = rect->top; y < rect->bottom; y++) {
for(x = rect->left; x < rect->right; x++) {
/* start lines pixels */
BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
BYTE *green = blue + 1;
BYTE *red = green + 1;
for(c = 0; c < 256; c++) {
if(*red == pal[c].peRed &&
*green == pal[c].peGreen &&
*blue == pal[c].peBlue)
{
*((BYTE *) dest + y * width + x) = c;
break;
}
}
}
}
HeapFree(GetProcessHeap(), 0, mem);
}
}
static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
IWineD3DSwapChainImpl *swapchain = NULL;
TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
if (!(This->Flags & SFLAG_LOCKABLE)) {
/* Note: UpdateTextures calls CopyRects which calls this routine to populate the
texture regions, and since the destination is an unlockable region we need
to tolerate this */
TRACE("Warning: trying to lock unlockable surf@%p\n", This);
/*return WINED3DERR_INVALIDCALL; */
}
pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);
/* Mark the surface locked */
This->Flags |= SFLAG_LOCKED;
/* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
if(!This->resource.allocatedMemory) {
This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
This->Flags &= ~SFLAG_INSYSMEM; /* This is the marker that surface data has to be downloaded */
}
/* Calculate the correct start address to report */
if (NULL == pRect) {
pLockedRect->pBits = This->resource.allocatedMemory;
This->lockedRect.left = 0;
This->lockedRect.top = 0;
This->lockedRect.right = This->currentDesc.Width;
This->lockedRect.bottom = This->currentDesc.Height;
TRACE("Locked Rect (%p) = l %d, t %d, r %d, b %d\n", &This->lockedRect, This->lockedRect.left, This->lockedRect.top, This->lockedRect.right, This->lockedRect.bottom);
} else {
TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
/* DXTn textures are based on compressed blocks of 4x4 pixels, each
* 16 bytes large (8 bytes in case of DXT1). Because of that Pitch has
* slightly different meaning compared to regular textures. For DXTn
* textures Pitch is the size of a row of blocks, 4 high and "width"
* long. The x offset is calculated differently as well, since moving 4
* pixels to the right actually moves an entire 4x4 block to right, ie
* 16 bytes (8 in case of DXT1). */
if (This->resource.format == WINED3DFMT_DXT1) {
pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 2);
} else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3
|| This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 4);
} else {
pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
}
This->lockedRect.left = pRect->left;
This->lockedRect.top = pRect->top;
This->lockedRect.right = pRect->right;
This->lockedRect.bottom = pRect->bottom;
}
if (This->Flags & SFLAG_NONPOW2) {
TRACE("Locking non-power 2 texture\n");
}
/* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
* This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
* changed
*/
if(!(This->Flags & SFLAG_DYNLOCK)) {
This->lockCount++;
/* MAXLOCKCOUNT is defined in wined3d_private.h */
if(This->lockCount > MAXLOCKCOUNT) {
TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
This->Flags |= SFLAG_DYNLOCK;
}
}
if (Flags & WINED3DLOCK_DISCARD) {
/* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
This->Flags |= SFLAG_INSYSMEM;
}
if (This->Flags & SFLAG_INSYSMEM) {
TRACE("Local copy is up to date, not downloading data\n");
goto lock_end;
}
/* Now download the surface content from opengl
* Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
* Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
*/
IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
if(swapchain || iface == myDevice->render_targets[0]) {
BOOL srcIsUpsideDown;
if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
static BOOL warned = FALSE;
if(!warned) {
ERR("The application tries to lock the render target, but render target locking is disabled\n");
warned = TRUE;
}
if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
return WINED3D_OK;
}
/* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
* Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
* should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
* context->last_was_blit set on the unlock.
*/
ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
ENTER_GL();
/* Select the correct read buffer, and give some debug output.
* There is no need to keep track of the current read buffer or reset it, every part of the code
* that reads sets the read buffer as desired.
*/
if(!swapchain) {
/* Locking the primary render target which is not on a swapchain(=offscreen render target).
* Read from the back buffer
*/
TRACE("Locking offscreen render target\n");
glReadBuffer(myDevice->offscreenBuffer);
srcIsUpsideDown = TRUE;
} else {
GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
TRACE("Locking %#x buffer\n", buffer);
glReadBuffer(buffer);
checkGLcall("glReadBuffer");
IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
srcIsUpsideDown = FALSE;
}
switch(wined3d_settings.rendertargetlock_mode) {
case RTL_AUTO:
case RTL_READDRAW:
case RTL_READTEX:
read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
break;
case RTL_TEXDRAW:
case RTL_TEXTEX:
read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
break;
}
LEAVE_GL();
/* Mark the local copy up to date if a full download was done */
if(This->lockedRect.left == 0 &&
This->lockedRect.top == 0 &&
This->lockedRect.right == This->currentDesc.Width &&
This->lockedRect.bottom == This->currentDesc.Height) {
This->Flags |= SFLAG_INSYSMEM;
}
} else if(iface == myDevice->stencilBufferTarget) {
/** the depth stencil in openGL has a format of GL_FLOAT
* which should be good for WINED3DFMT_D16_LOCKABLE
* and WINED3DFMT_D16
* it is unclear what format the stencil buffer is in except.
* 'Each index is converted to fixed point...
* If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
* mappings in the table GL_PIXEL_MAP_S_TO_S.
* glReadPixels(This->lockedRect.left,
* This->lockedRect.bottom - j - 1,
* This->lockedRect.right - This->lockedRect.left,
* 1,
* GL_DEPTH_COMPONENT,
* type,
* (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
*
* Depth Stencil surfaces which are not the current depth stencil target should have their data in a
* gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
* none of that is the case the problem is not in this function :-)
********************************************/
FIXME("Depth stencil locking not supported yet\n");
} else {
/* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
TRACE("locking an ordinary surface\n");
if (0 != This->glDescription.textureName) {
/* Now I have to copy thing bits back */
if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
}
ENTER_GL();
/* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
if (GL_SUPPORT(ARB_MULTITEXTURE)) {
GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
checkGLcall("glActiveTextureARB");
}
IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
IWineD3DSurface_PreLoad(iface);
surface_download_data(This);
LEAVE_GL();
}
}
lock_end:
if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
/* Don't dirtify */
} else {
IWineD3DBaseTexture *pBaseTexture;
/**
* Dirtify on lock
* as seen in msdn docs
*/
IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
/** Dirtify Container if needed */
if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
TRACE("Making container dirty\n");
IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
IWineD3DBaseTexture_Release(pBaseTexture);
} else {
TRACE("Surface is standalone, no need to dirty the container\n");
}
}
TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch,
This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
return WINED3D_OK;
}
static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
GLint prev_store;
GLint prev_rasterpos[4];
GLint skipBytes = 0;
BOOL storechanged = FALSE, memory_allocated = FALSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -