blob: 138eab38863ffa664ae6ad7cefcd57c2ef8a25c2 [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/bitset_utils.h"
#include "common/debug.h"
#include "libANGLE/Context.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Texture.h"
#include "libANGLE/renderer/d3d/TextureD3D.h"
#include "libANGLE/renderer/d3d/d3d11/Buffer11.h"
#include "libANGLE/renderer/d3d/d3d11/Clear11.h"
#include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h"
#include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
#include "libANGLE/renderer/d3d/d3d11/TextureStorage11.h"
#include "libANGLE/renderer/d3d/d3d11/formatutils11.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
using namespace angle;
namespace rx
{
namespace
{
angle::Result MarkAttachmentsDirty(const gl::Context *context,
const gl::FramebufferAttachment *attachment)
{
if (attachment->type() == GL_TEXTURE)
{
gl::Texture *texture = attachment->getTexture();
TextureD3D *textureD3D = GetImplAs<TextureD3D>(texture);
TextureStorage *texStorage = nullptr;
ANGLE_TRY(textureD3D->getNativeTexture(context, &texStorage));
if (texStorage)
{
TextureStorage11 *texStorage11 = GetAs<TextureStorage11>(texStorage);
ASSERT(texStorage11);
texStorage11->markLevelDirty(attachment->mipLevel());
}
}
return angle::Result::Continue;
}
} // anonymous namespace
Framebuffer11::Framebuffer11(const gl::FramebufferState &data, Renderer11 *renderer)
: FramebufferD3D(data, renderer), mRenderer(renderer)
{
ASSERT(mRenderer != nullptr);
}
Framebuffer11::~Framebuffer11() {}
angle::Result Framebuffer11::markAttachmentsDirty(const gl::Context *context) const
{
const auto &colorAttachments = mState.getColorAttachments();
for (size_t drawBuffer : mState.getEnabledDrawBuffers())
{
const gl::FramebufferAttachment &colorAttachment = colorAttachments[drawBuffer];
ASSERT(colorAttachment.isAttached());
ANGLE_TRY(MarkAttachmentsDirty(context, &colorAttachment));
}
const gl::FramebufferAttachment *dsAttachment = mState.getDepthOrStencilAttachment();
if (dsAttachment)
{
ANGLE_TRY(MarkAttachmentsDirty(context, dsAttachment));
}
return angle::Result::Continue;
}
angle::Result Framebuffer11::clearImpl(const gl::Context *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(context, presentPathFastClearParams, mState));
}
else
{
ANGLE_TRY(clearer->clearFramebuffer(context, clearParams, mState));
}
ANGLE_TRY(markAttachmentsDirty(context));
return angle::Result::Continue;
}
angle::Result Framebuffer11::invalidate(const gl::Context *context,
size_t count,
const GLenum *attachments)
{
return invalidateBase(context, count, attachments, false);
}
angle::Result Framebuffer11::discard(const gl::Context *context,
size_t count,
const GLenum *attachments)
{
return invalidateBase(context, count, attachments, true);
}
angle::Result Framebuffer11::invalidateBase(const gl::Context *context,
size_t count,
const GLenum *attachments,
bool useEXTBehavior) const
{
ID3D11DeviceContext1 *deviceContext1 = mRenderer->getDeviceContext1IfSupported();
if (!deviceContext1)
{
// DiscardView() is only supported on ID3D11DeviceContext1
return angle::Result::Continue;
}
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));
const gl::FramebufferAttachment *colorAttachment =
mState.getColorAttachment(colorIndex);
if (colorAttachment)
{
ANGLE_TRY(invalidateAttachment(context, 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(context, mState.getDepthAttachment()));
}
if (discardStencil && mState.getStencilAttachment())
{
ANGLE_TRY(invalidateAttachment(context, mState.getStencilAttachment()));
}
return angle::Result::Continue;
}
angle::Result Framebuffer11::invalidateSub(const gl::Context *context,
size_t count,
const GLenum *attachments,
const gl::Rectangle &area)
{
// A no-op implementation conforms to the spec, so don't call UNIMPLEMENTED()
return angle::Result::Continue;
}
angle::Result Framebuffer11::invalidateAttachment(const gl::Context *context,
const gl::FramebufferAttachment *attachment) const
{
ID3D11DeviceContext1 *deviceContext1 = mRenderer->getDeviceContext1IfSupported();
ASSERT(deviceContext1);
ASSERT(attachment && attachment->isAttached());
RenderTarget11 *renderTarget = nullptr;
ANGLE_TRY(attachment->getRenderTarget(context, 0, &renderTarget));
const auto &rtv = renderTarget->getRenderTargetView();
if (rtv.valid())
{
deviceContext1->DiscardView(rtv.get());
}
return angle::Result::Continue;
}
angle::Result Framebuffer11::readPixelsImpl(const gl::Context *context,
const gl::Rectangle &area,
GLenum format,
GLenum type,
size_t outputPitch,
const gl::PixelPackState &pack,
uint8_t *pixels)
{
const gl::FramebufferAttachment *readAttachment = mState.getReadAttachment();
ASSERT(readAttachment);
gl::Buffer *packBuffer = context->getState().getTargetBuffer(gl::BufferBinding::PixelPack);
if (packBuffer != nullptr)
{
Buffer11 *packBufferStorage = GetImplAs<Buffer11>(packBuffer);
const angle::Format &angleFormat = GetFormatFromFormatType(format, type);
PackPixelsParams packParams(area, angleFormat, static_cast<GLuint>(outputPitch),
pack.reverseRowOrder, packBuffer,
reinterpret_cast<ptrdiff_t>(pixels));
return packBufferStorage->packPixels(context, *readAttachment, packParams);
}
return mRenderer->readFromAttachment(context, *readAttachment, area, format, type,
static_cast<GLuint>(outputPitch), pack, pixels);
}
angle::Result Framebuffer11::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)
{
if (blitRenderTarget)
{
const gl::FramebufferAttachment *readBuffer = sourceFramebuffer->getReadColorAttachment();
ASSERT(readBuffer);
RenderTargetD3D *readRenderTarget = nullptr;
ANGLE_TRY(readBuffer->getRenderTarget(context, 0, &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(
context, drawBuffer.getRenderToTextureSamples(), &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(
context, actualSourceArea, actualDestArea, readRenderTarget, drawRenderTarget,
filter, scissor, blitRenderTarget, false, false));
}
}
}
if (blitDepth || blitStencil)
{
const gl::FramebufferAttachment *readBuffer =
sourceFramebuffer->getDepthOrStencilAttachment();
ASSERT(readBuffer);
RenderTargetD3D *readRenderTarget = nullptr;
ANGLE_TRY(readBuffer->getRenderTarget(context, 0, &readRenderTarget));
ASSERT(readRenderTarget);
const bool invertSource = UsePresentPathFast(mRenderer, readBuffer);
gl::Rectangle actualSourceArea = sourceArea;
if (invertSource)
{
RenderTarget11 *readRenderTarget11 = GetAs<RenderTarget11>(readRenderTarget);
actualSourceArea.y = readRenderTarget11->getHeight() - sourceArea.y;
actualSourceArea.height = -sourceArea.height;
}
const gl::FramebufferAttachment *drawBuffer = mState.getDepthOrStencilAttachment();
ASSERT(drawBuffer);
RenderTargetD3D *drawRenderTarget = nullptr;
ANGLE_TRY(drawBuffer->getRenderTarget(context, drawBuffer->getRenderToTextureSamples(),
&drawRenderTarget));
ASSERT(drawRenderTarget);
bool invertDest = UsePresentPathFast(mRenderer, drawBuffer);
gl::Rectangle actualDestArea = destArea;
if (invertDest)
{
RenderTarget11 *drawRenderTarget11 = GetAs<RenderTarget11>(drawRenderTarget);
actualDestArea.y = drawRenderTarget11->getHeight() - destArea.y;
actualDestArea.height = -destArea.height;
}
ANGLE_TRY(mRenderer->blitRenderbufferRect(context, actualSourceArea, actualDestArea,
readRenderTarget, drawRenderTarget, filter,
scissor, false, blitDepth, blitStencil));
}
ANGLE_TRY(markAttachmentsDirty(context));
return angle::Result::Continue;
}
GLenum Framebuffer11::getRenderTargetImplementationFormat(RenderTargetD3D *renderTarget) const
{
RenderTarget11 *renderTarget11 = GetAs<RenderTarget11>(renderTarget);
return renderTarget11->getFormatSet().format().fboImplementationInternalFormat;
}
angle::Result Framebuffer11::syncState(const gl::Context *context,
const gl::Framebuffer::DirtyBits &dirtyBits)
{
ANGLE_TRY(mRenderTargetCache.update(context, mState, dirtyBits));
ANGLE_TRY(FramebufferD3D::syncState(context, dirtyBits));
// Call this last to allow the state manager to take advantage of the cached render targets.
mRenderer->getStateManager()->invalidateRenderTarget();
// Call this to syncViewport for framebuffer default parameters.
if (mState.getDefaultWidth() != 0 || mState.getDefaultHeight() != 0)
{
mRenderer->getStateManager()->invalidateViewport(context);
}
return angle::Result::Continue;
}
angle::Result Framebuffer11::getSamplePosition(const gl::Context *context,
size_t index,
GLfloat *xy) const
{
const gl::FramebufferAttachment *attachment = mState.getFirstNonNullAttachment();
ASSERT(attachment);
GLsizei sampleCount = attachment->getSamples();
rx::GetSamplePosition(sampleCount, index, xy);
return angle::Result::Continue;
}
RenderTarget11 *Framebuffer11::getFirstRenderTarget() const
{
for (auto *renderTarget : mRenderTargetCache.getColors())
{
if (renderTarget)
{
return renderTarget;
}
}
return mRenderTargetCache.getDepthStencil();
}
} // namespace rx