// // Copyright 2014 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // Framebuffer9.cpp: Implements the Framebuffer9 class. #include "libANGLE/renderer/d3d/d3d9/Framebuffer9.h" #include "libANGLE/Context.h" #include "libANGLE/Framebuffer.h" #include "libANGLE/FramebufferAttachment.h" #include "libANGLE/Texture.h" #include "libANGLE/formatutils.h" #include "libANGLE/renderer/ContextImpl.h" #include "libANGLE/renderer/d3d/TextureD3D.h" #include "libANGLE/renderer/d3d/d3d9/RenderTarget9.h" #include "libANGLE/renderer/d3d/d3d9/Renderer9.h" #include "libANGLE/renderer/d3d/d3d9/TextureStorage9.h" #include "libANGLE/renderer/d3d/d3d9/formatutils9.h" #include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h" #include "libANGLE/renderer/renderer_utils.h" namespace rx { Framebuffer9::Framebuffer9(const gl::FramebufferState &data, Renderer9 *renderer) : FramebufferD3D(data, renderer), mRenderer(renderer) { ASSERT(mRenderer != nullptr); } Framebuffer9::~Framebuffer9() {} angle::Result Framebuffer9::discard(const gl::Context *context, size_t count, const GLenum *attachments) { ANGLE_HR_UNREACHABLE(GetImplAs(context)); return angle::Result::Stop; } angle::Result Framebuffer9::invalidate(const gl::Context *context, size_t count, const GLenum *attachments) { ANGLE_HR_UNREACHABLE(GetImplAs(context)); return angle::Result::Stop; } angle::Result Framebuffer9::invalidateSub(const gl::Context *context, size_t count, const GLenum *attachments, const gl::Rectangle &area) { ANGLE_HR_UNREACHABLE(GetImplAs(context)); return angle::Result::Stop; } angle::Result Framebuffer9::clearImpl(const gl::Context *context, const ClearParameters &clearParams) { ANGLE_TRY(mRenderer->applyRenderTarget(context, mRenderTargetCache.getColors()[0], mRenderTargetCache.getDepthStencil())); const gl::State &glState = context->getState(); float nearZ = glState.getNearPlane(); float farZ = glState.getFarPlane(); mRenderer->setViewport(glState.getViewport(), nearZ, farZ, gl::PrimitiveMode::Triangles, glState.getRasterizerState().frontFace, true); mRenderer->setScissorRectangle(glState.getScissor(), glState.isScissorTestEnabled()); mRenderer->clear(clearParams, mRenderTargetCache.getColors()[0], mRenderTargetCache.getDepthStencil()); return angle::Result::Continue; } angle::Result Framebuffer9::readPixelsImpl(const gl::Context *context, const gl::Rectangle &area, GLenum format, GLenum type, size_t outputPitch, const gl::PixelPackState &pack, gl::Buffer *packBuffer, uint8_t *pixels) { const gl::FramebufferAttachment *colorbuffer = mState.getColorAttachment(0); ASSERT(colorbuffer); RenderTarget9 *renderTarget = nullptr; ANGLE_TRY(colorbuffer->getRenderTarget(context, 0, &renderTarget)); ASSERT(renderTarget); IDirect3DSurface9 *surface = renderTarget->getSurface(); ASSERT(surface); D3DSURFACE_DESC desc; surface->GetDesc(&desc); Context9 *context9 = GetImplAs(context); if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) { UNIMPLEMENTED(); // FIXME: Requires resolve using StretchRect into non-multisampled render // target SafeRelease(surface); ANGLE_TRY_HR(context9, E_OUTOFMEMORY, "ReadPixels is unimplemented for multisampled framebuffer attachments."); } IDirect3DDevice9 *device = mRenderer->getDevice(); ASSERT(device); HRESULT result; IDirect3DSurface9 *systemSurface = nullptr; bool directToPixels = !pack.reverseRowOrder && pack.alignment <= 4 && mRenderer->getShareHandleSupport() && area.x == 0 && area.y == 0 && static_cast(area.width) == desc.Width && static_cast(area.height) == desc.Height && desc.Format == D3DFMT_A8R8G8B8 && format == GL_BGRA_EXT && type == GL_UNSIGNED_BYTE; if (directToPixels) { // Use the pixels ptr as a shared handle to write directly into client's memory result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, reinterpret_cast(&pixels)); if (FAILED(result)) { // Try again without the shared handle directToPixels = false; } } if (!directToPixels) { result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, nullptr); if (FAILED(result)) { ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); SafeRelease(surface); ANGLE_TRY_HR(context9, E_OUTOFMEMORY, "Failed to allocate internal texture for ReadPixels."); } } result = device->GetRenderTargetData(surface, systemSurface); SafeRelease(surface); if (FAILED(result)) { SafeRelease(systemSurface); // It turns out that D3D will sometimes produce more error // codes than those documented. if (d3d9::isDeviceLostError(result)) { mRenderer->notifyDeviceLost(); } else { UNREACHABLE(); } ANGLE_TRY_HR(context9, E_OUTOFMEMORY, "Failed to read internal render target data."); } if (directToPixels) { SafeRelease(systemSurface); return angle::Result::Continue; } RECT rect; rect.left = gl::clamp(area.x, 0L, static_cast(desc.Width)); rect.top = gl::clamp(area.y, 0L, static_cast(desc.Height)); rect.right = gl::clamp(area.x + area.width, 0L, static_cast(desc.Width)); rect.bottom = gl::clamp(area.y + area.height, 0L, static_cast(desc.Height)); D3DLOCKED_RECT lock; result = systemSurface->LockRect(&lock, &rect, D3DLOCK_READONLY); if (FAILED(result)) { UNREACHABLE(); SafeRelease(systemSurface); ANGLE_TRY_HR(context9, E_OUTOFMEMORY, "Failed to lock internal render target."); } uint8_t *source = static_cast(lock.pBits); int inputPitch = lock.Pitch; const d3d9::D3DFormat &d3dFormatInfo = d3d9::GetD3DFormatInfo(desc.Format); gl::FormatType formatType(format, type); PackPixelsParams packParams; packParams.area.x = rect.left; packParams.area.y = rect.top; packParams.area.width = rect.right - rect.left; packParams.area.height = rect.bottom - rect.top; packParams.destFormat = &GetFormatFromFormatType(format, type); packParams.outputPitch = static_cast(outputPitch); packParams.reverseRowOrder = pack.reverseRowOrder; PackPixels(packParams, d3dFormatInfo.info(), inputPitch, source, pixels); systemSurface->UnlockRect(); SafeRelease(systemSurface); return angle::Result::Continue; } angle::Result Framebuffer9::blitImpl(const gl::Context *context, const gl::Rectangle &sourceArea, const gl::Rectangle &destArea, const gl::Rectangle *scissor, bool blitRenderTarget, bool blitDepth, bool blitStencil, GLenum filter, const gl::Framebuffer *sourceFramebuffer) { ASSERT(filter == GL_NEAREST); IDirect3DDevice9 *device = mRenderer->getDevice(); ASSERT(device); mRenderer->endScene(); Context9 *context9 = GetImplAs(context); if (blitRenderTarget) { const gl::FramebufferAttachment *readBuffer = sourceFramebuffer->getColorAttachment(0); ASSERT(readBuffer); RenderTarget9 *readRenderTarget = nullptr; ANGLE_TRY(readBuffer->getRenderTarget(context, 0, &readRenderTarget)); ASSERT(readRenderTarget); const gl::FramebufferAttachment *drawBuffer = mState.getColorAttachment(0); ASSERT(drawBuffer); RenderTarget9 *drawRenderTarget = nullptr; ANGLE_TRY( drawBuffer->getRenderTarget(context, drawBuffer->getSamples(), &drawRenderTarget)); ASSERT(drawRenderTarget); // The getSurface calls do an AddRef so save them until after no errors are possible IDirect3DSurface9 *readSurface = readRenderTarget->getSurface(); ASSERT(readSurface); IDirect3DSurface9 *drawSurface = drawRenderTarget->getSurface(); ASSERT(drawSurface); gl::Extents srcSize(readRenderTarget->getWidth(), readRenderTarget->getHeight(), 1); gl::Extents dstSize(drawRenderTarget->getWidth(), drawRenderTarget->getHeight(), 1); RECT srcRect; srcRect.left = sourceArea.x; srcRect.right = sourceArea.x + sourceArea.width; srcRect.top = sourceArea.y; srcRect.bottom = sourceArea.y + sourceArea.height; RECT dstRect; dstRect.left = destArea.x; dstRect.right = destArea.x + destArea.width; dstRect.top = destArea.y; dstRect.bottom = destArea.y + destArea.height; // Clip the rectangles to the scissor rectangle if (scissor) { if (dstRect.left < scissor->x) { srcRect.left += (scissor->x - dstRect.left); dstRect.left = scissor->x; } if (dstRect.top < scissor->y) { srcRect.top += (scissor->y - dstRect.top); dstRect.top = scissor->y; } if (dstRect.right > scissor->x + scissor->width) { srcRect.right -= (dstRect.right - (scissor->x + scissor->width)); dstRect.right = scissor->x + scissor->width; } if (dstRect.bottom > scissor->y + scissor->height) { srcRect.bottom -= (dstRect.bottom - (scissor->y + scissor->height)); dstRect.bottom = scissor->y + scissor->height; } } // Clip the rectangles to the destination size if (dstRect.left < 0) { srcRect.left += -dstRect.left; dstRect.left = 0; } if (dstRect.right > dstSize.width) { srcRect.right -= (dstRect.right - dstSize.width); dstRect.right = dstSize.width; } if (dstRect.top < 0) { srcRect.top += -dstRect.top; dstRect.top = 0; } if (dstRect.bottom > dstSize.height) { srcRect.bottom -= (dstRect.bottom - dstSize.height); dstRect.bottom = dstSize.height; } // Clip the rectangles to the source size if (srcRect.left < 0) { dstRect.left += -srcRect.left; srcRect.left = 0; } if (srcRect.right > srcSize.width) { dstRect.right -= (srcRect.right - srcSize.width); srcRect.right = srcSize.width; } if (srcRect.top < 0) { dstRect.top += -srcRect.top; srcRect.top = 0; } if (srcRect.bottom > srcSize.height) { dstRect.bottom -= (srcRect.bottom - srcSize.height); srcRect.bottom = srcSize.height; } HRESULT result = device->StretchRect(readSurface, &srcRect, drawSurface, &dstRect, D3DTEXF_NONE); SafeRelease(readSurface); SafeRelease(drawSurface); ANGLE_TRY_HR(context9, result, "Internal blit failed."); } if (blitDepth || blitStencil) { const gl::FramebufferAttachment *readBuffer = sourceFramebuffer->getDepthOrStencilAttachment(); ASSERT(readBuffer); RenderTarget9 *readDepthStencil = nullptr; ANGLE_TRY(readBuffer->getRenderTarget(context, 0, &readDepthStencil)); ASSERT(readDepthStencil); const gl::FramebufferAttachment *drawBuffer = mState.getDepthOrStencilAttachment(); ASSERT(drawBuffer); RenderTarget9 *drawDepthStencil = nullptr; ANGLE_TRY( drawBuffer->getRenderTarget(context, drawBuffer->getSamples(), &drawDepthStencil)); ASSERT(drawDepthStencil); // The getSurface calls do an AddRef so save them until after no errors are possible IDirect3DSurface9 *readSurface = readDepthStencil->getSurface(); ASSERT(readDepthStencil); IDirect3DSurface9 *drawSurface = drawDepthStencil->getSurface(); ASSERT(drawDepthStencil); HRESULT result = device->StretchRect(readSurface, nullptr, drawSurface, nullptr, D3DTEXF_NONE); SafeRelease(readSurface); SafeRelease(drawSurface); ANGLE_TRY_HR(context9, result, "Internal blit failed."); } return angle::Result::Continue; } const gl::InternalFormat &Framebuffer9::getImplementationColorReadFormat( const gl::Context *context) const { GLenum sizedFormat = mState.getReadAttachment()->getFormat().info->sizedInternalFormat; const d3d9::TextureFormat &textureFormat = d3d9::GetTextureFormatInfo(sizedFormat); const d3d9::D3DFormat &d3dFormatInfo = d3d9::GetD3DFormatInfo(textureFormat.renderFormat); const angle::Format &angleFormat = angle::Format::Get(d3dFormatInfo.formatID); return gl::GetSizedInternalFormatInfo(angleFormat.fboImplementationInternalFormat); } angle::Result Framebuffer9::getSamplePosition(const gl::Context *context, size_t index, GLfloat *xy) const { ANGLE_HR_UNREACHABLE(GetImplAs(context)); return angle::Result::Stop; } angle::Result Framebuffer9::syncState(const gl::Context *context, GLenum binding, const gl::Framebuffer::DirtyBits &dirtyBits, gl::Command command) { ANGLE_TRY(FramebufferD3D::syncState(context, binding, dirtyBits, command)); ANGLE_TRY(mRenderTargetCache.update(context, mState, dirtyBits)); return angle::Result::Continue; } } // namespace rx