|  | // | 
|  | // 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. | 
|  | // | 
|  |  | 
|  | // FramebufferD3D.cpp: Implements the DefaultAttachmentD3D and FramebufferD3D classes. | 
|  |  | 
|  | #include "libANGLE/renderer/d3d/FramebufferD3D.h" | 
|  |  | 
|  | #include "common/bitset_utils.h" | 
|  | #include "libANGLE/Context.h" | 
|  | #include "libANGLE/Framebuffer.h" | 
|  | #include "libANGLE/FramebufferAttachment.h" | 
|  | #include "libANGLE/Surface.h" | 
|  | #include "libANGLE/formatutils.h" | 
|  | #include "libANGLE/renderer/ContextImpl.h" | 
|  | #include "libANGLE/renderer/d3d/ContextD3D.h" | 
|  | #include "libANGLE/renderer/d3d/RenderTargetD3D.h" | 
|  | #include "libANGLE/renderer/d3d/RenderbufferD3D.h" | 
|  | #include "libANGLE/renderer/d3d/RendererD3D.h" | 
|  | #include "libANGLE/renderer/d3d/SurfaceD3D.h" | 
|  | #include "libANGLE/renderer/d3d/SwapChainD3D.h" | 
|  | #include "libANGLE/renderer/d3d/TextureD3D.h" | 
|  |  | 
|  | namespace rx | 
|  | { | 
|  |  | 
|  | namespace | 
|  | { | 
|  |  | 
|  | ClearParameters GetClearParameters(const gl::State &state, GLbitfield mask) | 
|  | { | 
|  | ClearParameters clearParams; | 
|  | memset(&clearParams, 0, sizeof(ClearParameters)); | 
|  |  | 
|  | const auto &blendState = state.getBlendState(); | 
|  |  | 
|  | for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++) | 
|  | { | 
|  | clearParams.clearColor[i] = false; | 
|  | } | 
|  | clearParams.colorF           = state.getColorClearValue(); | 
|  | clearParams.colorType        = GL_FLOAT; | 
|  | clearParams.colorMaskRed     = blendState.colorMaskRed; | 
|  | clearParams.colorMaskGreen   = blendState.colorMaskGreen; | 
|  | clearParams.colorMaskBlue    = blendState.colorMaskBlue; | 
|  | clearParams.colorMaskAlpha   = blendState.colorMaskAlpha; | 
|  | clearParams.clearDepth       = false; | 
|  | clearParams.depthValue       = state.getDepthClearValue(); | 
|  | clearParams.clearStencil     = false; | 
|  | clearParams.stencilValue     = state.getStencilClearValue(); | 
|  | clearParams.stencilWriteMask = state.getDepthStencilState().stencilWritemask; | 
|  | clearParams.scissorEnabled   = state.isScissorTestEnabled(); | 
|  | clearParams.scissor          = state.getScissor(); | 
|  |  | 
|  | const gl::Framebuffer *framebufferObject = state.getDrawFramebuffer(); | 
|  | if (mask & GL_COLOR_BUFFER_BIT) | 
|  | { | 
|  | if (framebufferObject->hasEnabledDrawBuffer()) | 
|  | { | 
|  | for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++) | 
|  | { | 
|  | clearParams.clearColor[i] = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (mask & GL_DEPTH_BUFFER_BIT) | 
|  | { | 
|  | if (state.getDepthStencilState().depthMask && | 
|  | framebufferObject->getDepthAttachment() != nullptr) | 
|  | { | 
|  | clearParams.clearDepth = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (mask & GL_STENCIL_BUFFER_BIT) | 
|  | { | 
|  | if (framebufferObject->getStencilAttachment() != nullptr && | 
|  | framebufferObject->getStencilAttachment()->getStencilSize() > 0) | 
|  | { | 
|  | clearParams.clearStencil = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return clearParams; | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | ClearParameters::ClearParameters() = default; | 
|  |  | 
|  | ClearParameters::ClearParameters(const ClearParameters &other) = default; | 
|  |  | 
|  | FramebufferD3D::FramebufferD3D(const gl::FramebufferState &data, RendererD3D *renderer) | 
|  | : FramebufferImpl(data), mRenderer(renderer), mDummyAttachment() | 
|  | {} | 
|  |  | 
|  | FramebufferD3D::~FramebufferD3D() {} | 
|  |  | 
|  | angle::Result FramebufferD3D::clear(const gl::Context *context, GLbitfield mask) | 
|  | { | 
|  | ClearParameters clearParams = GetClearParameters(context->getState(), mask); | 
|  | return clearImpl(context, clearParams); | 
|  | } | 
|  |  | 
|  | angle::Result FramebufferD3D::clearBufferfv(const gl::Context *context, | 
|  | GLenum buffer, | 
|  | GLint drawbuffer, | 
|  | const GLfloat *values) | 
|  | { | 
|  | // glClearBufferfv can be called to clear the color buffer or depth buffer | 
|  | ClearParameters clearParams = GetClearParameters(context->getState(), 0); | 
|  |  | 
|  | if (buffer == GL_COLOR) | 
|  | { | 
|  | for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++) | 
|  | { | 
|  | clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i)); | 
|  | } | 
|  | clearParams.colorF    = gl::ColorF(values[0], values[1], values[2], values[3]); | 
|  | clearParams.colorType = GL_FLOAT; | 
|  | } | 
|  |  | 
|  | if (buffer == GL_DEPTH) | 
|  | { | 
|  | clearParams.clearDepth = true; | 
|  | clearParams.depthValue = values[0]; | 
|  | } | 
|  |  | 
|  | return clearImpl(context, clearParams); | 
|  | } | 
|  |  | 
|  | angle::Result FramebufferD3D::clearBufferuiv(const gl::Context *context, | 
|  | GLenum buffer, | 
|  | GLint drawbuffer, | 
|  | const GLuint *values) | 
|  | { | 
|  | // glClearBufferuiv can only be called to clear a color buffer | 
|  | ClearParameters clearParams = GetClearParameters(context->getState(), 0); | 
|  | for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++) | 
|  | { | 
|  | clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i)); | 
|  | } | 
|  | clearParams.colorUI   = gl::ColorUI(values[0], values[1], values[2], values[3]); | 
|  | clearParams.colorType = GL_UNSIGNED_INT; | 
|  |  | 
|  | return clearImpl(context, clearParams); | 
|  | } | 
|  |  | 
|  | angle::Result FramebufferD3D::clearBufferiv(const gl::Context *context, | 
|  | GLenum buffer, | 
|  | GLint drawbuffer, | 
|  | const GLint *values) | 
|  | { | 
|  | // glClearBufferiv can be called to clear the color buffer or stencil buffer | 
|  | ClearParameters clearParams = GetClearParameters(context->getState(), 0); | 
|  |  | 
|  | if (buffer == GL_COLOR) | 
|  | { | 
|  | for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++) | 
|  | { | 
|  | clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i)); | 
|  | } | 
|  | clearParams.colorI    = gl::ColorI(values[0], values[1], values[2], values[3]); | 
|  | clearParams.colorType = GL_INT; | 
|  | } | 
|  |  | 
|  | if (buffer == GL_STENCIL) | 
|  | { | 
|  | clearParams.clearStencil = true; | 
|  | clearParams.stencilValue = values[0]; | 
|  | } | 
|  |  | 
|  | return clearImpl(context, clearParams); | 
|  | } | 
|  |  | 
|  | angle::Result FramebufferD3D::clearBufferfi(const gl::Context *context, | 
|  | GLenum buffer, | 
|  | GLint drawbuffer, | 
|  | GLfloat depth, | 
|  | GLint stencil) | 
|  | { | 
|  | // glClearBufferfi can only be called to clear a depth stencil buffer | 
|  | ClearParameters clearParams = GetClearParameters(context->getState(), 0); | 
|  | clearParams.clearDepth      = true; | 
|  | clearParams.depthValue      = depth; | 
|  | clearParams.clearStencil    = true; | 
|  | clearParams.stencilValue    = stencil; | 
|  |  | 
|  | return clearImpl(context, clearParams); | 
|  | } | 
|  |  | 
|  | GLenum FramebufferD3D::getImplementationColorReadFormat(const gl::Context *context) const | 
|  | { | 
|  | const gl::FramebufferAttachment *readAttachment = mState.getReadAttachment(); | 
|  |  | 
|  | if (readAttachment == nullptr) | 
|  | { | 
|  | return GL_NONE; | 
|  | } | 
|  |  | 
|  | RenderTargetD3D *attachmentRenderTarget = nullptr; | 
|  | angle::Result error                     = readAttachment->getRenderTarget( | 
|  | context, readAttachment->getRenderToTextureSamples(), &attachmentRenderTarget); | 
|  | if (error != angle::Result::Continue) | 
|  | { | 
|  | return GL_NONE; | 
|  | } | 
|  |  | 
|  | GLenum implementationFormat = getRenderTargetImplementationFormat(attachmentRenderTarget); | 
|  | const gl::InternalFormat &implementationFormatInfo = | 
|  | gl::GetSizedInternalFormatInfo(implementationFormat); | 
|  |  | 
|  | return implementationFormatInfo.getReadPixelsFormat(context->getExtensions()); | 
|  | } | 
|  |  | 
|  | GLenum FramebufferD3D::getImplementationColorReadType(const gl::Context *context) const | 
|  | { | 
|  | const gl::FramebufferAttachment *readAttachment = mState.getReadAttachment(); | 
|  |  | 
|  | if (readAttachment == nullptr) | 
|  | { | 
|  | return GL_NONE; | 
|  | } | 
|  |  | 
|  | RenderTargetD3D *attachmentRenderTarget = nullptr; | 
|  | angle::Result error                     = readAttachment->getRenderTarget( | 
|  | context, readAttachment->getRenderToTextureSamples(), &attachmentRenderTarget); | 
|  | if (error != angle::Result::Continue) | 
|  | { | 
|  | return GL_NONE; | 
|  | } | 
|  |  | 
|  | GLenum implementationFormat = getRenderTargetImplementationFormat(attachmentRenderTarget); | 
|  | const gl::InternalFormat &implementationFormatInfo = | 
|  | gl::GetSizedInternalFormatInfo(implementationFormat); | 
|  |  | 
|  | return implementationFormatInfo.getReadPixelsType(context->getClientVersion()); | 
|  | } | 
|  |  | 
|  | angle::Result FramebufferD3D::readPixels(const gl::Context *context, | 
|  | const gl::Rectangle &area, | 
|  | GLenum format, | 
|  | GLenum type, | 
|  | void *pixels) | 
|  | { | 
|  | // Clip read area to framebuffer. | 
|  | const gl::Extents fbSize = getState().getReadAttachment()->getSize(); | 
|  | const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height); | 
|  | gl::Rectangle clippedArea; | 
|  | if (!ClipRectangle(area, fbRect, &clippedArea)) | 
|  | { | 
|  | // nothing to read | 
|  | return angle::Result::Continue; | 
|  | } | 
|  |  | 
|  | const gl::PixelPackState &packState = context->getState().getPackState(); | 
|  |  | 
|  | const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(format, type); | 
|  |  | 
|  | ContextD3D *contextD3D = GetImplAs<ContextD3D>(context); | 
|  |  | 
|  | GLuint outputPitch = 0; | 
|  | ANGLE_CHECK_GL_MATH(contextD3D, | 
|  | sizedFormatInfo.computeRowPitch(type, area.width, packState.alignment, | 
|  | packState.rowLength, &outputPitch)); | 
|  |  | 
|  | GLuint outputSkipBytes = 0; | 
|  | ANGLE_CHECK_GL_MATH(contextD3D, sizedFormatInfo.computeSkipBytes( | 
|  | type, outputPitch, 0, packState, false, &outputSkipBytes)); | 
|  | outputSkipBytes += (clippedArea.x - area.x) * sizedFormatInfo.pixelBytes + | 
|  | (clippedArea.y - area.y) * outputPitch; | 
|  |  | 
|  | return readPixelsImpl(context, clippedArea, format, type, outputPitch, packState, | 
|  | static_cast<uint8_t *>(pixels) + outputSkipBytes); | 
|  | } | 
|  |  | 
|  | angle::Result FramebufferD3D::blit(const gl::Context *context, | 
|  | const gl::Rectangle &sourceArea, | 
|  | const gl::Rectangle &destArea, | 
|  | GLbitfield mask, | 
|  | GLenum filter) | 
|  | { | 
|  | const auto &glState                      = context->getState(); | 
|  | const gl::Framebuffer *sourceFramebuffer = glState.getReadFramebuffer(); | 
|  | const gl::Rectangle *scissor = glState.isScissorTestEnabled() ? &glState.getScissor() : nullptr; | 
|  | ANGLE_TRY(blitImpl(context, sourceArea, destArea, scissor, (mask & GL_COLOR_BUFFER_BIT) != 0, | 
|  | (mask & GL_DEPTH_BUFFER_BIT) != 0, (mask & GL_STENCIL_BUFFER_BIT) != 0, | 
|  | filter, sourceFramebuffer)); | 
|  |  | 
|  | return angle::Result::Continue; | 
|  | } | 
|  |  | 
|  | bool FramebufferD3D::checkStatus(const gl::Context *context) const | 
|  | { | 
|  | // if we have both a depth and stencil buffer, they must refer to the same object | 
|  | // since we only support packed_depth_stencil and not separate depth and stencil | 
|  | if (mState.hasSeparateDepthAndStencilAttachments()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // D3D11 does not allow for overlapping RenderTargetViews. | 
|  | // If WebGL compatibility is enabled, this has already been checked at a higher level. | 
|  | ASSERT(!context->getExtensions().webglCompatibility || | 
|  | mState.colorAttachmentsAreUniqueImages()); | 
|  | if (!context->getExtensions().webglCompatibility) | 
|  | { | 
|  | if (!mState.colorAttachmentsAreUniqueImages()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // D3D requires all render targets to have the same dimensions. | 
|  | if (!mState.attachmentsHaveSameDimensions()) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | angle::Result FramebufferD3D::syncState(const gl::Context *context, | 
|  | const gl::Framebuffer::DirtyBits &dirtyBits) | 
|  | { | 
|  | if (!mColorAttachmentsForRender.valid()) | 
|  | { | 
|  | return angle::Result::Continue; | 
|  | } | 
|  |  | 
|  | for (auto dirtyBit : dirtyBits) | 
|  | { | 
|  | if ((dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 && | 
|  | dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX) || | 
|  | dirtyBit == gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS) | 
|  | { | 
|  | mColorAttachmentsForRender.reset(); | 
|  | } | 
|  | } | 
|  |  | 
|  | return angle::Result::Continue; | 
|  | } | 
|  |  | 
|  | const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(const gl::Context *context) | 
|  | { | 
|  | gl::DrawBufferMask activeProgramOutputs = | 
|  | context->getState().getProgram()->getActiveOutputVariables(); | 
|  |  | 
|  | if (mColorAttachmentsForRender.valid() && mCurrentActiveProgramOutputs == activeProgramOutputs) | 
|  | { | 
|  | return mColorAttachmentsForRender.value(); | 
|  | } | 
|  |  | 
|  | // Does not actually free memory | 
|  | gl::AttachmentList colorAttachmentsForRender; | 
|  |  | 
|  | const auto &colorAttachments = mState.getColorAttachments(); | 
|  | const auto &drawBufferStates = mState.getDrawBufferStates(); | 
|  | const auto &features         = mRenderer->getFeatures(); | 
|  |  | 
|  | for (size_t attachmentIndex = 0; attachmentIndex < colorAttachments.size(); ++attachmentIndex) | 
|  | { | 
|  | GLenum drawBufferState                           = drawBufferStates[attachmentIndex]; | 
|  | const gl::FramebufferAttachment &colorAttachment = colorAttachments[attachmentIndex]; | 
|  |  | 
|  | if (colorAttachment.isAttached() && drawBufferState != GL_NONE && | 
|  | activeProgramOutputs[attachmentIndex]) | 
|  | { | 
|  | ASSERT(drawBufferState == GL_BACK || | 
|  | drawBufferState == (GL_COLOR_ATTACHMENT0_EXT + attachmentIndex)); | 
|  | colorAttachmentsForRender.push_back(&colorAttachment); | 
|  | } | 
|  | else if (!features.mrtPerfWorkaround.enabled) | 
|  | { | 
|  | colorAttachmentsForRender.push_back(nullptr); | 
|  | } | 
|  | } | 
|  |  | 
|  | // When rendering with no render target on D3D, two bugs lead to incorrect behavior on Intel | 
|  | // drivers < 4815. The rendering samples always pass neglecting discard statements in pixel | 
|  | // shader. We add a dummy texture as render target in such case. | 
|  | if (mRenderer->getFeatures().addDummyTextureNoRenderTarget.enabled && | 
|  | colorAttachmentsForRender.empty() && activeProgramOutputs.any()) | 
|  | { | 
|  | static_assert(static_cast<size_t>(activeProgramOutputs.size()) <= 32, | 
|  | "Size of active program outputs should less or equal than 32."); | 
|  | const GLuint activeProgramLocation = static_cast<GLuint>( | 
|  | gl::ScanForward(static_cast<uint32_t>(activeProgramOutputs.bits()))); | 
|  |  | 
|  | if (mDummyAttachment.isAttached() && | 
|  | (mDummyAttachment.getBinding() - GL_COLOR_ATTACHMENT0) == activeProgramLocation) | 
|  | { | 
|  | colorAttachmentsForRender.push_back(&mDummyAttachment); | 
|  | } | 
|  | else | 
|  | { | 
|  | // Remove dummy attachment to prevents us from leaking it, and the program may require | 
|  | // it to be attached to a new binding point. | 
|  | if (mDummyAttachment.isAttached()) | 
|  | { | 
|  | mDummyAttachment.detach(context); | 
|  | } | 
|  |  | 
|  | gl::Texture *dummyTex = nullptr; | 
|  | // TODO(Jamie): Handle error if dummy texture can't be created. | 
|  | (void)mRenderer->getIncompleteTexture(context, gl::TextureType::_2D, &dummyTex); | 
|  | if (dummyTex) | 
|  | { | 
|  |  | 
|  | gl::ImageIndex index = gl::ImageIndex::Make2D(0); | 
|  | mDummyAttachment     = gl::FramebufferAttachment( | 
|  | context, GL_TEXTURE, GL_COLOR_ATTACHMENT0_EXT + activeProgramLocation, index, | 
|  | dummyTex); | 
|  | colorAttachmentsForRender.push_back(&mDummyAttachment); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | mColorAttachmentsForRender   = std::move(colorAttachmentsForRender); | 
|  | mCurrentActiveProgramOutputs = activeProgramOutputs; | 
|  |  | 
|  | return mColorAttachmentsForRender.value(); | 
|  | } | 
|  |  | 
|  | void FramebufferD3D::destroy(const gl::Context *context) | 
|  | { | 
|  | if (mDummyAttachment.isAttached()) | 
|  | { | 
|  | mDummyAttachment.detach(context); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace rx |