| // |
| // 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. |
| // |
| |
| // Framebuffer11.cpp: Implements the Framebuffer11 class. |
| |
| #include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h" |
| |
| #include "common/debug.h" |
| #include "common/bitset_utils.h" |
| #include "libANGLE/renderer/d3d/d3d11/Buffer11.h" |
| #include "libANGLE/renderer/d3d/d3d11/Clear11.h" |
| #include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h" |
| #include "libANGLE/renderer/d3d/d3d11/Renderer11.h" |
| #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" |
| #include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h" |
| #include "libANGLE/renderer/d3d/d3d11/formatutils11.h" |
| #include "libANGLE/renderer/d3d/TextureD3D.h" |
| #include "libANGLE/Framebuffer.h" |
| #include "libANGLE/FramebufferAttachment.h" |
| #include "libANGLE/Texture.h" |
| |
| using namespace angle; |
| |
| namespace rx |
| { |
| |
| namespace |
| { |
| gl::Error MarkAttachmentsDirty(const gl::FramebufferAttachment *attachment) |
| { |
| if (attachment && attachment->type() == GL_TEXTURE) |
| { |
| gl::Texture *texture = attachment->getTexture(); |
| |
| TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture); |
| |
| TextureStorage *texStorage = nullptr; |
| ANGLE_TRY(textureD3D->getNativeTexture(&texStorage)); |
| |
| if (texStorage) |
| { |
| TextureStorage11 *texStorage11 = GetAs<TextureStorage11>(texStorage); |
| ASSERT(texStorage11); |
| |
| texStorage11->markLevelDirty(attachment->mipLevel()); |
| } |
| } |
| |
| return gl::NoError(); |
| } |
| |
| void UpdateCachedRenderTarget(const gl::FramebufferAttachment *attachment, |
| RenderTarget11 *&cachedRenderTarget, |
| OnRenderTargetDirtyBinding *channelBinding) |
| { |
| RenderTarget11 *newRenderTarget = nullptr; |
| if (attachment) |
| { |
| attachment->getRenderTarget(&newRenderTarget); |
| } |
| if (newRenderTarget != cachedRenderTarget) |
| { |
| auto channel = (newRenderTarget ? newRenderTarget->getBroadcastChannel() : nullptr); |
| channelBinding->bind(channel); |
| cachedRenderTarget = newRenderTarget; |
| } |
| } |
| } // anonymous namespace |
| |
| Framebuffer11::Framebuffer11(const gl::FramebufferState &data, Renderer11 *renderer) |
| : FramebufferD3D(data, renderer), |
| mRenderer(renderer), |
| mCachedDepthStencilRenderTarget(nullptr), |
| mDepthStencilRenderTargetDirty(this, gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS) |
| { |
| ASSERT(mRenderer != nullptr); |
| mCachedColorRenderTargets.fill(nullptr); |
| for (size_t colorIndex = 0; colorIndex < data.getColorAttachments().size(); ++colorIndex) |
| { |
| mColorRenderTargetsDirty.emplace_back(this, colorIndex); |
| } |
| } |
| |
| Framebuffer11::~Framebuffer11() |
| { |
| } |
| |
| gl::Error Framebuffer11::markAttachmentsDirty() const |
| { |
| for (const auto &colorAttachment : mState.getColorAttachments()) |
| { |
| if (colorAttachment.isAttached()) |
| { |
| ANGLE_TRY(MarkAttachmentsDirty(&colorAttachment)); |
| } |
| } |
| |
| ANGLE_TRY(MarkAttachmentsDirty(mState.getDepthAttachment())); |
| ANGLE_TRY(MarkAttachmentsDirty(mState.getStencilAttachment())); |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error Framebuffer11::clearImpl(ContextImpl *context, const ClearParameters &clearParams) |
| { |
| Clear11 *clearer = mRenderer->getClearer(); |
| |
| const gl::FramebufferAttachment *colorAttachment = mState.getFirstColorAttachment(); |
| if (clearParams.scissorEnabled == true && colorAttachment != nullptr && |
| UsePresentPathFast(mRenderer, colorAttachment)) |
| { |
| // If the current framebuffer is using the default colorbuffer, and present path fast is |
| // active, and the scissor rect is enabled, then we should invert the scissor rect |
| // vertically |
| ClearParameters presentPathFastClearParams = clearParams; |
| gl::Extents framebufferSize = colorAttachment->getSize(); |
| presentPathFastClearParams.scissor.y = framebufferSize.height - |
| presentPathFastClearParams.scissor.y - |
| presentPathFastClearParams.scissor.height; |
| ANGLE_TRY(clearer->clearFramebuffer(presentPathFastClearParams, mState)); |
| } |
| else |
| { |
| ANGLE_TRY(clearer->clearFramebuffer(clearParams, mState)); |
| } |
| |
| ANGLE_TRY(markAttachmentsDirty()); |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error Framebuffer11::invalidate(size_t count, const GLenum *attachments) |
| { |
| return invalidateBase(count, attachments, false); |
| } |
| |
| gl::Error Framebuffer11::discard(size_t count, const GLenum *attachments) |
| { |
| return invalidateBase(count, attachments, true); |
| } |
| |
| gl::Error Framebuffer11::invalidateBase(size_t count, const GLenum *attachments, bool useEXTBehavior) const |
| { |
| ID3D11DeviceContext1 *deviceContext1 = mRenderer->getDeviceContext1IfSupported(); |
| |
| if (!deviceContext1) |
| { |
| // DiscardView() is only supported on ID3D11DeviceContext1 |
| return gl::NoError(); |
| } |
| |
| bool foundDepth = false; |
| bool foundStencil = false; |
| |
| for (size_t i = 0; i < count; ++i) |
| { |
| switch (attachments[i]) |
| { |
| // Handle depth and stencil attachments. Defer discarding until later. |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| foundDepth = true; |
| foundStencil = true; |
| break; |
| case GL_DEPTH_EXT: |
| case GL_DEPTH_ATTACHMENT: |
| foundDepth = true; |
| break; |
| case GL_STENCIL_EXT: |
| case GL_STENCIL_ATTACHMENT: |
| foundStencil = true; |
| break; |
| default: |
| { |
| // Handle color attachments |
| ASSERT((attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15) || |
| (attachments[i] == GL_COLOR)); |
| |
| size_t colorIndex = |
| (attachments[i] == GL_COLOR ? 0u : (attachments[i] - GL_COLOR_ATTACHMENT0)); |
| auto colorAttachment = mState.getColorAttachment(colorIndex); |
| if (colorAttachment) |
| { |
| ANGLE_TRY(invalidateAttachment(colorAttachment)); |
| } |
| break; |
| } |
| } |
| } |
| |
| bool discardDepth = false; |
| bool discardStencil = false; |
| |
| // The D3D11 renderer uses the same view for depth and stencil buffers, so we must be careful. |
| if (useEXTBehavior) |
| { |
| // In the extension, if the app discards only one of the depth and stencil attachments, but |
| // those are backed by the same packed_depth_stencil buffer, then both images become undefined. |
| discardDepth = foundDepth; |
| |
| // Don't bother discarding the stencil buffer if the depth buffer will already do it |
| discardStencil = foundStencil && (!discardDepth || mState.getDepthAttachment() == nullptr); |
| } |
| else |
| { |
| // In ES 3.0.4, if a specified attachment has base internal format DEPTH_STENCIL but the |
| // attachments list does not include DEPTH_STENCIL_ATTACHMENT or both DEPTH_ATTACHMENT and |
| // STENCIL_ATTACHMENT, then only the specified portion of every pixel in the subregion of pixels |
| // of the DEPTH_STENCIL buffer may be invalidated, and the other portion must be preserved. |
| discardDepth = (foundDepth && foundStencil) || |
| (foundDepth && (mState.getStencilAttachment() == nullptr)); |
| discardStencil = (foundStencil && (mState.getDepthAttachment() == nullptr)); |
| } |
| |
| if (discardDepth && mState.getDepthAttachment()) |
| { |
| ANGLE_TRY(invalidateAttachment(mState.getDepthAttachment())); |
| } |
| |
| if (discardStencil && mState.getStencilAttachment()) |
| { |
| ANGLE_TRY(invalidateAttachment(mState.getStencilAttachment())); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error Framebuffer11::invalidateSub(size_t, const GLenum *, const gl::Rectangle &) |
| { |
| // A no-op implementation conforms to the spec, so don't call UNIMPLEMENTED() |
| return gl::NoError(); |
| } |
| |
| gl::Error Framebuffer11::invalidateAttachment(const gl::FramebufferAttachment *attachment) const |
| { |
| ID3D11DeviceContext1 *deviceContext1 = mRenderer->getDeviceContext1IfSupported(); |
| ASSERT(deviceContext1); |
| ASSERT(attachment && attachment->isAttached()); |
| |
| RenderTarget11 *renderTarget = nullptr; |
| ANGLE_TRY(attachment->getRenderTarget(&renderTarget)); |
| const auto &rtv = renderTarget->getRenderTargetView(); |
| |
| if (rtv.valid()) |
| { |
| deviceContext1->DiscardView(rtv.get()); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error Framebuffer11::readPixelsImpl(const gl::Rectangle &area, |
| GLenum format, |
| GLenum type, |
| size_t outputPitch, |
| const gl::PixelPackState &pack, |
| uint8_t *pixels) const |
| { |
| const gl::FramebufferAttachment *readAttachment = mState.getReadAttachment(); |
| ASSERT(readAttachment); |
| |
| gl::Buffer *packBuffer = pack.pixelBuffer.get(); |
| if (packBuffer != nullptr) |
| { |
| Buffer11 *packBufferStorage = GetImplAs<Buffer11>(packBuffer); |
| PackPixelsParams packParams(area, format, type, static_cast<GLuint>(outputPitch), pack, |
| reinterpret_cast<ptrdiff_t>(pixels)); |
| |
| return packBufferStorage->packPixels(*readAttachment, packParams); |
| } |
| |
| return mRenderer->readFromAttachment(*readAttachment, area, format, type, |
| static_cast<GLuint>(outputPitch), pack, pixels); |
| } |
| |
| gl::Error Framebuffer11::blitImpl(const gl::Rectangle &sourceArea, |
| const gl::Rectangle &destArea, |
| const gl::Rectangle *scissor, |
| bool blitRenderTarget, |
| bool blitDepth, |
| bool blitStencil, |
| GLenum filter, |
| const gl::Framebuffer *sourceFramebuffer) |
| { |
| if (blitRenderTarget) |
| { |
| const gl::FramebufferAttachment *readBuffer = sourceFramebuffer->getReadColorbuffer(); |
| ASSERT(readBuffer); |
| |
| RenderTargetD3D *readRenderTarget = nullptr; |
| ANGLE_TRY(readBuffer->getRenderTarget(&readRenderTarget)); |
| ASSERT(readRenderTarget); |
| |
| const auto &colorAttachments = mState.getColorAttachments(); |
| const auto &drawBufferStates = mState.getDrawBufferStates(); |
| |
| for (size_t colorAttachment = 0; colorAttachment < colorAttachments.size(); colorAttachment++) |
| { |
| const gl::FramebufferAttachment &drawBuffer = colorAttachments[colorAttachment]; |
| |
| if (drawBuffer.isAttached() && |
| drawBufferStates[colorAttachment] != GL_NONE) |
| { |
| RenderTargetD3D *drawRenderTarget = nullptr; |
| ANGLE_TRY(drawBuffer.getRenderTarget(&drawRenderTarget)); |
| ASSERT(drawRenderTarget); |
| |
| const bool invertColorSource = UsePresentPathFast(mRenderer, readBuffer); |
| gl::Rectangle actualSourceArea = sourceArea; |
| if (invertColorSource) |
| { |
| RenderTarget11 *readRenderTarget11 = GetAs<RenderTarget11>(readRenderTarget); |
| actualSourceArea.y = readRenderTarget11->getHeight() - sourceArea.y; |
| actualSourceArea.height = -sourceArea.height; |
| } |
| |
| const bool invertColorDest = UsePresentPathFast(mRenderer, &drawBuffer); |
| gl::Rectangle actualDestArea = destArea; |
| if (invertColorDest) |
| { |
| RenderTarget11 *drawRenderTarget11 = GetAs<RenderTarget11>(drawRenderTarget); |
| actualDestArea.y = drawRenderTarget11->getHeight() - destArea.y; |
| actualDestArea.height = -destArea.height; |
| } |
| |
| ANGLE_TRY(mRenderer->blitRenderbufferRect( |
| actualSourceArea, actualDestArea, readRenderTarget, drawRenderTarget, filter, |
| scissor, blitRenderTarget, false, false)); |
| } |
| } |
| } |
| |
| if (blitDepth || blitStencil) |
| { |
| const gl::FramebufferAttachment *readBuffer = sourceFramebuffer->getDepthOrStencilbuffer(); |
| ASSERT(readBuffer); |
| |
| RenderTargetD3D *readRenderTarget = nullptr; |
| ANGLE_TRY(readBuffer->getRenderTarget(&readRenderTarget)); |
| ASSERT(readRenderTarget); |
| |
| const gl::FramebufferAttachment *drawBuffer = mState.getDepthOrStencilAttachment(); |
| ASSERT(drawBuffer); |
| |
| RenderTargetD3D *drawRenderTarget = nullptr; |
| ANGLE_TRY(drawBuffer->getRenderTarget(&drawRenderTarget)); |
| ASSERT(drawRenderTarget); |
| |
| ANGLE_TRY(mRenderer->blitRenderbufferRect(sourceArea, destArea, readRenderTarget, |
| drawRenderTarget, filter, scissor, false, |
| blitDepth, blitStencil)); |
| } |
| |
| ANGLE_TRY(markAttachmentsDirty()); |
| return gl::NoError(); |
| } |
| |
| GLenum Framebuffer11::getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const |
| { |
| RenderTarget11 *renderTarget11 = GetAs<RenderTarget11>(renderTarget); |
| return renderTarget11->getFormatSet().format().fboImplementationInternalFormat; |
| } |
| |
| void Framebuffer11::updateColorRenderTarget(size_t colorIndex) |
| { |
| UpdateCachedRenderTarget(mState.getColorAttachment(colorIndex), |
| mCachedColorRenderTargets[colorIndex], |
| &mColorRenderTargetsDirty[colorIndex]); |
| } |
| |
| void Framebuffer11::updateDepthStencilRenderTarget() |
| { |
| UpdateCachedRenderTarget(mState.getDepthOrStencilAttachment(), mCachedDepthStencilRenderTarget, |
| &mDepthStencilRenderTargetDirty); |
| } |
| |
| void Framebuffer11::syncState(ContextImpl *contextImpl, const gl::Framebuffer::DirtyBits &dirtyBits) |
| { |
| mRenderer->getStateManager()->invalidateRenderTarget(); |
| |
| const auto &mergedDirtyBits = dirtyBits | mInternalDirtyBits; |
| mInternalDirtyBits.reset(); |
| |
| for (auto dirtyBit : mergedDirtyBits) |
| { |
| switch (dirtyBit) |
| { |
| case gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT: |
| case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT: |
| updateDepthStencilRenderTarget(); |
| break; |
| case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS: |
| case gl::Framebuffer::DIRTY_BIT_READ_BUFFER: |
| break; |
| default: |
| { |
| ASSERT(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0 && |
| dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX); |
| size_t colorIndex = |
| static_cast<size_t>(dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0); |
| updateColorRenderTarget(colorIndex); |
| break; |
| } |
| } |
| } |
| |
| // We should not have dirtied any additional state during our sync. |
| ASSERT(!mInternalDirtyBits.any()); |
| |
| FramebufferD3D::syncState(contextImpl, dirtyBits); |
| } |
| |
| void Framebuffer11::signal(size_t channelID) |
| { |
| if (channelID == gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS) |
| { |
| // Stencil is redundant in this case. |
| mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT); |
| mCachedDepthStencilRenderTarget = nullptr; |
| } |
| else |
| { |
| mInternalDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 + channelID); |
| mCachedColorRenderTargets[channelID] = nullptr; |
| } |
| } |
| |
| gl::Error Framebuffer11::getSamplePosition(size_t index, GLfloat *xy) const |
| { |
| UNIMPLEMENTED(); |
| return gl::InternalError() << "getSamplePosition is unimplemented."; |
| } |
| |
| bool Framebuffer11::hasAnyInternalDirtyBit() const |
| { |
| return mInternalDirtyBits.any(); |
| } |
| |
| void Framebuffer11::syncInternalState(ContextImpl *contextImpl) |
| { |
| syncState(contextImpl, gl::Framebuffer::DirtyBits()); |
| } |
| |
| } // namespace rx |