blob: 4cbabbc2a864fdefcf8af305a5e446d4facf13d4 [file] [log] [blame]
//
// 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