diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index 17e3ae6172..1ed6ea6614 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx @@ -1347,15 +1347,15 @@ framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr, const return; } - RECT SrcRect; + RECT src_rect; - SrcRect.left = xo; - SrcRect.right = xo+w; - SrcRect.top = yo; - SrcRect.bottom = yo+h; + src_rect.left = xo; + src_rect.right = xo+w; + src_rect.top = yo; + src_rect.bottom = yo+h; // now copy from fb to tex - hr = _d3d_device->CopyRects(render_target, &SrcRect, 1, tex_level_0, 0); + hr = _d3d_device->CopyRects(render_target, &src_rect, 1, tex_level_0, 0); if (FAILED(hr)) { dxgsg8_cat.error() << "CopyRects failed in copy_texture" << D3DERRORSTRING(hr); @@ -1392,73 +1392,105 @@ framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr, const Rend rect.left = xo; rect.right = xo + w; rect.bottom = yo + h; + bool copy_inverted = false; - IDirect3DSurface8 *framebuffer; + IDirect3DSurface8 *temp_surface = NULL; HRESULT hr; + // Note if you try to grab the backbuffer and full-screen + // anti-aliasing is on, the backbuffer might be larger than the + // window size. For screenshots it's safer to get the front buffer. if (_cur_read_pixel_buffer & RenderBuffer::T_back) { - hr = _d3d_device->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &framebuffer); + IDirect3DSurface8 *backbuffer = NULL; + // GetRenderTarget() seems to be a little more reliable than + // GetBackBuffer(). Might just be related to the swap_chain + // thing. + hr = _d3d_device->GetRenderTarget(&backbuffer); if (FAILED(hr)) { - dxgsg8_cat.error() << "GetBackBuffer failed" << D3DERRORSTRING(hr); + dxgsg8_cat.error() << "GetRenderTarget failed" << D3DERRORSTRING(hr); return false; } - // note if you try to grab the backbuffer and full-screen - // anti-aliasing is on, the backbuffer might be larger than the - // window size. for screenshots its safer to get the front - // buffer. + // Since we might not be able to Lock the back buffer, we will + // need to copy it to a temporary surface of the appropriate type + // first. + hr = _d3d_device->CreateImageSurface(w, h, _screen->_display_mode.Format, + &temp_surface); + if (FAILED(hr)) { + dxgsg8_cat.error() + << "CreateImageSurface failed in copy_pixel_buffer()" + << D3DERRORSTRING(hr); + backbuffer->Release(); + return false; + } + + // Now we must copy from the backbuffer to our temporary surface. + hr = _d3d_device->CopyRects(backbuffer, &rect, 1, temp_surface, NULL); + if (FAILED(hr)) { + dxgsg8_cat.error() << "CopyRects failed" << D3DERRORSTRING(hr); + temp_surface->Release(); + backbuffer->Release(); + return false; + } + + RELEASE(backbuffer, dxgsg8, "backbuffer", RELEASE_ONCE); } else if (_cur_read_pixel_buffer & RenderBuffer::T_front) { - // must create a A8R8G8B8 sysmem surface for GetFrontBuffer to - // copy to - - DWORD temp_x_size, temp_y_size; if (_screen->_presentation_params.Windowed) { - // GetFrontBuffer retrieves the entire desktop for a monitor, so - // need space for that - + // GetFrontBuffer() retrieves the entire desktop for a monitor, + // so we need to reserve space for that. + + // We have to use GetMonitorInfo(), since this GSG may not be + // for the primary monitor. MONITORINFO minfo; minfo.cbSize = sizeof(MONITORINFO); - GetMonitorInfo(_screen->_monitor, &minfo); // have to use GetMonitorInfo, since this gsg may not be for primary monitor - - temp_x_size = RECT_XSIZE(minfo.rcMonitor); - temp_y_size = RECT_YSIZE(minfo.rcMonitor); - + GetMonitorInfo(_screen->_monitor, &minfo); + + w = RECT_XSIZE(minfo.rcMonitor); + h = RECT_YSIZE(minfo.rcMonitor); + // set rect to client area of window in scrn coords ClientToScreen(_screen->_window, (POINT*)&rect.left); ClientToScreen(_screen->_window, (POINT*)&rect.right); - - } else { - RECT wind_rect; - GetWindowRect(_screen->_window, &wind_rect); - temp_x_size = RECT_XSIZE(wind_rect); - temp_y_size = RECT_YSIZE(wind_rect); } - - hr = _d3d_device->CreateImageSurface(temp_x_size, temp_y_size, D3DFMT_A8R8G8B8, &framebuffer); + + // For GetFrontBuffer(), we need a temporary surface of type + // A8R8G8B8. Unlike GetBackBuffer(), GetFrontBuffer() implicitly + // performs a copy. + hr = _d3d_device->CreateImageSurface(w, h, D3DFMT_A8R8G8B8, &temp_surface); if (FAILED(hr)) { - dxgsg8_cat.error() << "CreateImageSurface failed in copy_pixel_buffer()" << D3DERRORSTRING(hr); + dxgsg8_cat.error() + << "CreateImageSurface failed in copy_pixel_buffer()" + << D3DERRORSTRING(hr); return false; } - hr = _d3d_device->GetFrontBuffer(framebuffer); + hr = _d3d_device->GetFrontBuffer(temp_surface); if (hr == D3DERR_DEVICELOST) { - framebuffer->Release(); - dxgsg8_cat.error() << "copy_pixel_buffer failed: device lost\n"; + dxgsg8_cat.error() + << "copy_pixel_buffer failed: device lost\n"; + temp_surface->Release(); return false; } + // For some reason the front buffer comes out inverted, but the + // back buffer does not. + copy_inverted = true; + } else { - dxgsg8_cat.error() << "copy_pixel_buffer: unhandled current_read_pixel_buffer type\n"; + dxgsg8_cat.error() + << "copy_pixel_buffer: unhandled current_read_pixel_buffer type\n"; + temp_surface->Release(); return false; } - DXTextureContext8::d3d_surface_to_texture(rect, framebuffer, tex); + DXTextureContext8::d3d_surface_to_texture(rect, temp_surface, + copy_inverted, tex); - RELEASE(framebuffer, dxgsg8, "framebuffer", RELEASE_ONCE); + RELEASE(temp_surface, dxgsg8, "temp_surface", RELEASE_ONCE); nassertr(tex->has_ram_image(), false); return true; diff --git a/panda/src/dxgsg8/dxTextureContext8.cxx b/panda/src/dxgsg8/dxTextureContext8.cxx index f053e3165d..1236aa4892 100644 --- a/panda/src/dxgsg8/dxTextureContext8.cxx +++ b/panda/src/dxgsg8/dxTextureContext8.cxx @@ -710,7 +710,8 @@ delete_texture() { // texture //////////////////////////////////////////////////////////////////// HRESULT DXTextureContext8:: -d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface8 *d3d_surface, Texture *result) { +d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface8 *d3d_surface, + bool inverted, Texture *result) { // still need custom conversion since d3d/d3dx has no way to convert // arbitrary fmt to ARGB in-memory user buffer @@ -722,7 +723,8 @@ d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface8 *d3d_surface, Textur nassertr((num_components == 3) || (num_components == 4), E_FAIL); // cant handle anything else now nassertr(IS_VALID_PTR(d3d_surface), E_FAIL); - BYTE *buf = result->modify_ram_image().p(); + PTA_uchar ram_image = result->modify_ram_image(); + BYTE *buf = ram_image.p(); if (IsBadWritePtr(d3d_surface, sizeof(DWORD))) { dxgsg8_cat.error() @@ -748,12 +750,15 @@ d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface8 *d3d_surface, Textur if (!((copy_width == result->get_x_size()) && (copy_height <= (DWORD)result->get_y_size()))) { dxgsg8_cat.error() - << "d3d_surface_to_texture, Texture size too small to hold display surface!\n"; + << "d3d_surface_to_texture, Texture size (" << result->get_x_size() + << ", " << result->get_y_size() + << ") too small to hold display surface (" + << copy_width << ", " << copy_height << ")\n"; nassertr(false, E_FAIL); return E_FAIL; } - hr = d3d_surface->LockRect(&locked_rect, (CONST RECT*)NULL, (D3DLOCK_READONLY | D3DLOCK_NO_DIRTY_UPDATE /* | D3DLOCK_NOSYSLOCK */)); + hr = d3d_surface->LockRect(&locked_rect, (CONST RECT*)NULL, (D3DLOCK_READONLY | D3DLOCK_NO_DIRTY_UPDATE)); if (FAILED(hr)) { dxgsg8_cat.error() << "d3d_surface_to_texture LockRect() failed!" << D3DERRORSTRING(hr); @@ -771,16 +776,24 @@ d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface8 *d3d_surface, Textur //buf contains raw ARGB in Texture byteorder - DWORD byte_pitch = locked_rect.Pitch; + int byte_pitch = locked_rect.Pitch; BYTE *surface_bytes = (BYTE *)locked_rect.pBits; + if (inverted) { + surface_bytes += byte_pitch * (y_window_offset + copy_height - 1); + byte_pitch = -byte_pitch; + } else { + surface_bytes += byte_pitch * y_window_offset; + } + // writes out last line in DDSurf first in PixelBuf, so Y line order // precedes inversely if (dxgsg8_cat.is_debug()) { dxgsg8_cat.debug() - << "d3d_surface_to_texture converting " << D3DFormatStr(surface_desc.Format) - << "bpp DDSurf to " << num_components << "-channel panda Texture\n"; + << "d3d_surface_to_texture converting " + << D3DFormatStr(surface_desc.Format) + << " DDSurf to " << num_components << "-channel panda Texture\n"; } DWORD *dest_word = (DWORD *)buf; @@ -793,20 +806,19 @@ d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface8 *d3d_surface, Textur DWORD *source_word; BYTE *dest_line = (BYTE*)dest_word; - surface_bytes += byte_pitch*(y_window_offset+copy_height-1); - for (DWORD y = 0; y> 8) & g_LowByteMask); b = (BYTE)((pixel ) & g_LowByteMask); - *dest_byte += b; - *dest_byte += g; - *dest_byte += r; + *dest_byte++ = b; + *dest_byte++ = g; + *dest_byte++ = r; + source_word++; } + surface_bytes += byte_pitch; } } break; @@ -825,12 +839,11 @@ d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface8 *d3d_surface, Textur case D3DFMT_R8G8B8: { BYTE *source_byte; - surface_bytes += byte_pitch*(y_window_offset+copy_height-1); if (num_components == 4) { - for (DWORD y = 0; yDevCaps & D3DDEVCAPS_HWRASTERIZATION); - presentation_params->BackBufferFormat = Display._display_mode.Format; // dont need dest alpha, so just use adapter format + presentation_params->BackBufferFormat = display._display_mode.Format; // dont need dest alpha, so just use adapter format bool do_sync = sync_video; @@ -509,28 +509,28 @@ create_screen_buffers_and_device(DXScreenData &Display, bool force_16bpp_zbuffer } // verify the rendertarget fmt one last time - if (FAILED(_d3d8->CheckDeviceFormat(Display._card_id, D3DDEVTYPE_HAL, Display._display_mode.Format, D3DUSAGE_RENDERTARGET, + if (FAILED(_d3d8->CheckDeviceFormat(display._card_id, D3DDEVTYPE_HAL, display._display_mode.Format, D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, presentation_params->BackBufferFormat))) { - wdxdisplay8_cat.error() << "device #" << Display._card_id << " CheckDeviceFmt failed for surface fmt " << D3DFormatStr(presentation_params->BackBufferFormat) << endl; + wdxdisplay8_cat.error() << "device #" << display._card_id << " CheckDeviceFmt failed for surface fmt " << D3DFormatStr(presentation_params->BackBufferFormat) << endl; goto Fallback_to_16bpp_buffers; } - if (FAILED(_d3d8->CheckDeviceType(Display._card_id, D3DDEVTYPE_HAL, Display._display_mode.Format, presentation_params->BackBufferFormat, + if (FAILED(_d3d8->CheckDeviceType(display._card_id, D3DDEVTYPE_HAL, display._display_mode.Format, presentation_params->BackBufferFormat, is_fullscreen()))) { - wdxdisplay8_cat.error() << "device #" << Display._card_id << " CheckDeviceType failed for surface fmt " << D3DFormatStr(presentation_params->BackBufferFormat) << endl; + wdxdisplay8_cat.error() << "device #" << display._card_id << " CheckDeviceType failed for surface fmt " << D3DFormatStr(presentation_params->BackBufferFormat) << endl; goto Fallback_to_16bpp_buffers; } - if (Display._presentation_params.EnableAutoDepthStencil) { - if (!dxpipe->find_best_depth_format(Display, Display._display_mode, - &Display._presentation_params.AutoDepthStencilFormat, + if (display._presentation_params.EnableAutoDepthStencil) { + if (!dxpipe->find_best_depth_format(display, display._display_mode, + &display._presentation_params.AutoDepthStencilFormat, bWantStencil, false)) { wdxdisplay8_cat.error() << "find_best_depth_format failed in CreateScreenBuffers for device #" - << Display._card_id << endl; + << display._card_id << endl; goto Fallback_to_16bpp_buffers; } - _depth_buffer_bpp = D3DFMT_to_DepthBits(Display._presentation_params.AutoDepthStencilFormat); + _depth_buffer_bpp = D3DFMT_to_DepthBits(display._presentation_params.AutoDepthStencilFormat); } else { _depth_buffer_bpp = 0; } @@ -539,18 +539,18 @@ create_screen_buffers_and_device(DXScreenData &Display, bool force_16bpp_zbuffer if (dx_multisample_antialiasing_level>1) { // need to check both rendertarget and zbuffer fmts - hr = _d3d8->CheckDeviceMultiSampleType(Display._card_id, D3DDEVTYPE_HAL, Display._display_mode.Format, + hr = _d3d8->CheckDeviceMultiSampleType(display._card_id, D3DDEVTYPE_HAL, display._display_mode.Format, is_fullscreen(), D3DMULTISAMPLE_TYPE(dx_multisample_antialiasing_level.get_value())); if (FAILED(hr)) { - wdxdisplay8_cat.fatal() << "device #" << Display._card_id << " doesnt support multisample level " << dx_multisample_antialiasing_level << "surface fmt " << D3DFormatStr(Display._display_mode.Format) << endl; + wdxdisplay8_cat.fatal() << "device #" << display._card_id << " doesnt support multisample level " << dx_multisample_antialiasing_level << "surface fmt " << D3DFormatStr(display._display_mode.Format) << endl; return false; } - if (Display._presentation_params.EnableAutoDepthStencil) { - hr = _d3d8->CheckDeviceMultiSampleType(Display._card_id, D3DDEVTYPE_HAL, Display._presentation_params.AutoDepthStencilFormat, + if (display._presentation_params.EnableAutoDepthStencil) { + hr = _d3d8->CheckDeviceMultiSampleType(display._card_id, D3DDEVTYPE_HAL, display._presentation_params.AutoDepthStencilFormat, is_fullscreen(), D3DMULTISAMPLE_TYPE(dx_multisample_antialiasing_level.get_value())); if (FAILED(hr)) { - wdxdisplay8_cat.fatal() << "device #" << Display._card_id << " doesnt support multisample level " << dx_multisample_antialiasing_level << "surface fmt " << D3DFormatStr(Display._presentation_params.AutoDepthStencilFormat) << endl; + wdxdisplay8_cat.fatal() << "device #" << display._card_id << " doesnt support multisample level " << dx_multisample_antialiasing_level << "surface fmt " << D3DFormatStr(display._presentation_params.AutoDepthStencilFormat) << endl; return false; } } @@ -558,14 +558,14 @@ create_screen_buffers_and_device(DXScreenData &Display, bool force_16bpp_zbuffer presentation_params->MultiSampleType = D3DMULTISAMPLE_TYPE(dx_multisample_antialiasing_level.get_value()); if (wdxdisplay8_cat.is_info()) - wdxdisplay8_cat.info() << "device #" << Display._card_id << " using multisample antialiasing level " << dx_multisample_antialiasing_level << endl; + wdxdisplay8_cat.info() << "device #" << display._card_id << " using multisample antialiasing level " << dx_multisample_antialiasing_level << endl; } presentation_params->BackBufferCount = 1; presentation_params->Flags = 0x0; - presentation_params->hDeviceWindow = Display._window; - presentation_params->BackBufferWidth = Display._display_mode.Width; - presentation_params->BackBufferHeight = Display._display_mode.Height; + presentation_params->hDeviceWindow = display._window; + presentation_params->BackBufferWidth = display._display_mode.Width; + presentation_params->BackBufferHeight = display._display_mode.Height; if (_wcontext._is_tnl_device) { dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; @@ -587,22 +587,22 @@ create_screen_buffers_and_device(DXScreenData &Display, bool force_16bpp_zbuffer // false, causing us to go into a 'wait-for WM_ACTIVATEAPP true' // loop, and the event never comes so we hang in fullscreen wait. // also doing this for windowed mode since it was requested. - if (!SetForegroundWindow(Display._window)) { + if (!SetForegroundWindow(display._window)) { wdxdisplay8_cat.warning() << "SetForegroundWindow() failed!\n"; } if (is_fullscreen()) { presentation_params->SwapEffect = D3DSWAPEFFECT_DISCARD; // we dont care about preserving contents of old frame presentation_params->FullScreen_PresentationInterval = (do_sync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE); - presentation_params->FullScreen_RefreshRateInHz = Display._display_mode.RefreshRate; + presentation_params->FullScreen_RefreshRateInHz = display._display_mode.RefreshRate; - ClearToBlack(Display._window, get_properties()); + ClearToBlack(display._window, get_properties()); - hr = _d3d8->CreateDevice(Display._card_id, D3DDEVTYPE_HAL, _hWnd, - dwBehaviorFlags, presentation_params, &Display._d3d_device); + hr = _d3d8->CreateDevice(display._card_id, D3DDEVTYPE_HAL, _hWnd, + dwBehaviorFlags, presentation_params, &display._d3d_device); if (FAILED(hr)) { - wdxdisplay8_cat.fatal() << "D3D CreateDevice failed for device #" << Display._card_id << ", " << D3DERRORSTRING(hr); + wdxdisplay8_cat.fatal() << "D3D CreateDevice failed for device #" << display._card_id << ", " << D3DERRORSTRING(hr); if (hr == D3DERR_OUTOFVIDEOMEMORY) goto Fallback_to_16bpp_buffers; @@ -616,7 +616,7 @@ create_screen_buffers_and_device(DXScreenData &Display, bool force_16bpp_zbuffer else { // CREATE WINDOWED BUFFERS D3DDISPLAYMODE dispmode; - hr = Display._d3d8->GetAdapterDisplayMode(Display._card_id, &dispmode); + hr = display._d3d8->GetAdapterDisplayMode(display._card_id, &dispmode); if (FAILED(hr)) { wdxdisplay8_cat.fatal() << "GetAdapterDisplayMode failed" << D3DERRORSTRING(hr); @@ -642,15 +642,15 @@ create_screen_buffers_and_device(DXScreenData &Display, bool force_16bpp_zbuffer //assert((dwRenderWidth == presentation_params->BackBufferWidth)&&(dwRenderHeight == presentation_params->BackBufferHeight)); - hr = _d3d8->CreateDevice(Display._card_id, D3DDEVTYPE_HAL, _hWnd, - dwBehaviorFlags, presentation_params, &Display._d3d_device); + hr = _d3d8->CreateDevice(display._card_id, D3DDEVTYPE_HAL, _hWnd, + dwBehaviorFlags, presentation_params, &display._d3d_device); if (FAILED(hr)) { wdxdisplay8_cat.warning() << "presentation_params->BackBufferWidth : " << presentation_params->BackBufferWidth << endl; wdxdisplay8_cat.warning() << "presentation_params->BackBufferHeight : " << presentation_params->BackBufferHeight << endl; wdxdisplay8_cat.warning() << "presentation_params->BackBufferFormat : " << presentation_params->BackBufferFormat << endl; wdxdisplay8_cat.warning() << "presentation_params->BackBufferCount : " << presentation_params->BackBufferCount << endl; - wdxdisplay8_cat.warning() << "D3D CreateDevice failed for device #" << Display._card_id << D3DERRORSTRING(hr); + wdxdisplay8_cat.warning() << "D3D CreateDevice failed for device #" << display._card_id << D3DERRORSTRING(hr); goto Fallback_to_16bpp_buffers; } } // end create windowed buffers @@ -673,26 +673,26 @@ create_screen_buffers_and_device(DXScreenData &Display, bool force_16bpp_zbuffer Fallback_to_16bpp_buffers: if ((!IS_16BPP_DISPLAY_FORMAT(presentation_params->BackBufferFormat)) && - (Display._supported_screen_depths_mask & (R5G6B5_FLAG|X1R5G5B5_FLAG))) { + (display._supported_screen_depths_mask & (R5G6B5_FLAG|X1R5G5B5_FLAG))) { // fallback strategy, if we trying >16bpp, fallback to 16bpp buffers - Display._display_mode.Format = ((Display._supported_screen_depths_mask & R5G6B5_FLAG) ? D3DFMT_R5G6B5 : D3DFMT_X1R5G5B5); + display._display_mode.Format = ((display._supported_screen_depths_mask & R5G6B5_FLAG) ? D3DFMT_R5G6B5 : D3DFMT_X1R5G5B5); if (wdxdisplay8_cat.info()) { wdxdisplay8_cat.info() << "CreateDevice failed with out-of-vidmem or invalid BackBufferFormat, retrying w/16bpp buffers on device #" - << Display._card_id << endl; + << display._card_id << endl; } - return create_screen_buffers_and_device(Display, true); + return create_screen_buffers_and_device(display, true); //return; } else if (!((dwRenderWidth == 640)&&(dwRenderHeight == 480))) { if (wdxdisplay8_cat.info()) - wdxdisplay8_cat.info() << "CreateDevice failed w/out-of-vidmem, retrying at 640x480 w/16bpp buffers on device #" << Display._card_id << endl; + wdxdisplay8_cat.info() << "CreateDevice failed w/out-of-vidmem, retrying at 640x480 w/16bpp buffers on device #" << display._card_id << endl; // try final fallback to 640x480x16 - Display._display_mode.Width = 640; - Display._display_mode.Height = 480; - return create_screen_buffers_and_device(Display, true); + display._display_mode.Width = 640; + display._display_mode.Height = 480; + return create_screen_buffers_and_device(display, true); //return; } else { diff --git a/panda/src/dxgsg8/wdxGraphicsWindow8.h b/panda/src/dxgsg8/wdxGraphicsWindow8.h index e8770c48e4..5e8318d468 100644 --- a/panda/src/dxgsg8/wdxGraphicsWindow8.h +++ b/panda/src/dxgsg8/wdxGraphicsWindow8.h @@ -65,7 +65,7 @@ private: }; typedef pvector DXDeviceInfoVec; - bool create_screen_buffers_and_device(DXScreenData &Display, + bool create_screen_buffers_and_device(DXScreenData &display, bool force_16bpp_zbuffer); bool choose_device();