1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2024-12-02 19:24:12 +01:00

[dxvk] Reimplemented clearRenderTarget

Closer to the D3D11 API. We cannot use the normal clearColorImage and
clearDepthStencilImage methods in case the game uses a 2D array view
for a 3D image. Fixes some validation issues in Hellblade.
This commit is contained in:
Philip Rebohle 2018-03-17 17:59:43 +01:00
parent 1af52abb67
commit 52a9a4f406
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
6 changed files with 169 additions and 139 deletions

View File

@ -473,62 +473,35 @@ namespace dxvk {
if (rtv == nullptr)
return;
// Find out whether the given attachment is currently bound
// or not, and if it is, which attachment index it has.
int32_t attachmentIndex = -1;
for (uint32_t i = 0; i < m_state.om.renderTargetViews.size(); i++) {
if (m_state.om.renderTargetViews.at(i) == rtv)
attachmentIndex = i;
}
// Copy the clear color into a clear value structure.
// This should also work for images that don nott have
// a floating point format.
const Rc<DxvkImageView> view = rtv->GetImageView();
VkClearColorValue clearValue;
std::memcpy(clearValue.float32, ColorRGBA,
sizeof(clearValue.float32));
VkClearValue clearValue;
clearValue.color.float32[0] = ColorRGBA[0];
clearValue.color.float32[1] = ColorRGBA[1];
clearValue.color.float32[2] = ColorRGBA[2];
clearValue.color.float32[3] = ColorRGBA[3];
if (attachmentIndex >= 0) {
// Image is bound to the pipeline for rendering. We can
// use the clear function that operates on attachments.
VkClearAttachment clearInfo;
clearInfo.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clearInfo.colorAttachment = static_cast<uint32_t>(attachmentIndex);
clearInfo.clearValue.color = clearValue;
VkClearRect clearRect;
clearRect.rect.offset.x = 0;
clearRect.rect.offset.y = 0;
clearRect.rect.extent.width = view->mipLevelExtent(0).width;
clearRect.rect.extent.height = view->mipLevelExtent(0).height;
clearRect.baseArrayLayer = 0;
clearRect.layerCount = view->info().numLayers;
// Clear the full area. On FL 9.x, only the first array
// layer will be cleared, rather than all array layers.
VkClearRect clearRect;
clearRect.rect.offset.x = 0;
clearRect.rect.offset.y = 0;
clearRect.rect.extent.width = view->mipLevelExtent(0).width;
clearRect.rect.extent.height = view->mipLevelExtent(0).height;
clearRect.baseArrayLayer = 0;
clearRect.layerCount = view->info().numLayers;
if (m_parent->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0)
clearRect.layerCount = 1;
if (m_parent->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0)
clearRect.layerCount = 1;
EmitCs([
cClearInfo = clearInfo,
cClearRect = clearRect
] (DxvkContext* ctx) {
ctx->clearRenderTarget(cClearInfo, cClearRect);
});
} else {
// Image is not bound to the pipeline. We can still clear
// it, but we'll have to use a generic clear function.
EmitCs([
cClearValue = clearValue,
cDstView = view
] (DxvkContext* ctx) {
ctx->clearColorImage(cDstView->image(),
cClearValue, cDstView->subresources());
});
}
EmitCs([
cClearValue = clearValue,
cClearRect = clearRect,
cImageView = view
] (DxvkContext* ctx) {
ctx->clearRenderTarget(
cImageView, cClearRect,
VK_IMAGE_ASPECT_COLOR_BIT,
cClearValue);
});
}
@ -606,53 +579,33 @@ namespace dxvk {
if (ClearFlags & D3D11_CLEAR_STENCIL)
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
const DxvkFormatInfo* formatInfo =
imageFormatInfo(view->info().format);
aspectMask &= formatInfo->aspectMask;
aspectMask &= imageFormatInfo(view->info().format)->aspectMask;
VkClearDepthStencilValue clearValue;
clearValue.depth = Depth;
clearValue.stencil = Stencil;
VkClearValue clearValue;
clearValue.depthStencil.depth = Depth;
clearValue.depthStencil.stencil = Stencil;
if (m_state.om.depthStencilView == dsv) {
// Image is bound to the pipeline for rendering. We can
// use the clear function that operates on attachments.
VkClearAttachment clearInfo;
clearInfo.aspectMask = aspectMask;
clearInfo.colorAttachment = 0;
clearInfo.clearValue.depthStencil = clearValue;
VkClearRect clearRect;
clearRect.rect.offset.x = 0;
clearRect.rect.offset.y = 0;
clearRect.rect.extent.width = view->mipLevelExtent(0).width;
clearRect.rect.extent.height = view->mipLevelExtent(0).height;
clearRect.baseArrayLayer = 0;
clearRect.layerCount = view->info().numLayers;
// Clear the full area
VkClearRect clearRect;
clearRect.rect.offset.x = 0;
clearRect.rect.offset.y = 0;
clearRect.rect.extent.width = view->mipLevelExtent(0).width;
clearRect.rect.extent.height = view->mipLevelExtent(0).height;
clearRect.baseArrayLayer = 0;
clearRect.layerCount = view->info().numLayers;
if (m_parent->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0)
clearRect.layerCount = 1;
if (m_parent->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0)
clearRect.layerCount = 1;
EmitCs([
cClearInfo = clearInfo,
cClearRect = clearRect
] (DxvkContext* ctx) {
ctx->clearRenderTarget(cClearInfo, cClearRect);
});
} else {
EmitCs([
cClearValue = clearValue,
cDstView = view,
cAspectMask = aspectMask
] (DxvkContext* ctx) {
VkImageSubresourceRange subresources = cDstView->subresources();
subresources.aspectMask = cAspectMask;
ctx->clearDepthStencilImage(cDstView->image(),
cClearValue, subresources);
});
}
EmitCs([
cClearValue = clearValue,
cClearRect = clearRect,
cAspectMask = aspectMask,
cImageView = view
] (DxvkContext* ctx) {
ctx->clearRenderTarget(
cImageView, cClearRect,
cAspectMask, cClearValue);
});
}

View File

@ -311,15 +311,56 @@ namespace dxvk {
void DxvkContext::clearRenderTarget(
const VkClearAttachment& attachment,
const VkClearRect& clearArea) {
// We only need the framebuffer to be bound. Flushing the
// entire pipeline state is not required and might actually
// cause problems if the current pipeline state is invalid.
this->renderPassBegin();
const Rc<DxvkImageView>& imageView,
const VkClearRect& clearRect,
VkImageAspectFlags clearAspects,
const VkClearValue& clearValue) {
// Check whether the render target view is an attachment
// of the current framebuffer. If not, we need to create
// a temporary framebuffer.
uint32_t attachmentIndex = MaxNumRenderTargets;
if (m_state.om.framebuffer != nullptr)
attachmentIndex = m_state.om.framebuffer->findAttachment(imageView);
if (attachmentIndex == MaxNumRenderTargets) {
this->renderPassEnd();
// Set up and bind a temporary framebuffer
DxvkRenderTargets attachments;
if (clearAspects & VK_IMAGE_ASPECT_COLOR_BIT) {
attachments.setColorTarget(0, imageView,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
} else {
attachments.setDepthTarget(imageView,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}
this->renderPassBindFramebuffer(
m_device->createFramebuffer(attachments));
} else {
// Make sure that the currently bound
// framebuffer can be rendered to
this->renderPassBegin();
}
// Clear the attachment in quesion
VkClearAttachment clearInfo;
clearInfo.aspectMask = clearAspects;
clearInfo.colorAttachment = attachmentIndex;
clearInfo.clearValue = clearValue;
if (attachmentIndex == MaxNumRenderTargets)
clearInfo.colorAttachment = 0;
m_cmd->cmdClearAttachments(
1, &attachment, 1, &clearArea);
1, &clearInfo, 1, &clearRect);
// If we used a temporary framebuffer, we'll have to unbind it
// again in order to not disturb subsequent rendering commands.
if (attachmentIndex == MaxNumRenderTargets)
this->renderPassUnbindFramebuffer();
}
@ -1298,27 +1339,7 @@ namespace dxvk {
if (!m_flags.test(DxvkContextFlag::GpRenderPassBound)
&& (m_state.om.framebuffer != nullptr)) {
m_flags.set(DxvkContextFlag::GpRenderPassBound);
const DxvkFramebufferSize fbSize
= m_state.om.framebuffer->size();
VkRect2D renderArea;
renderArea.offset = VkOffset2D { 0, 0 };
renderArea.extent = VkExtent2D { fbSize.width, fbSize.height };
VkRenderPassBeginInfo info;
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.pNext = nullptr;
info.renderPass = m_state.om.framebuffer->renderPass();
info.framebuffer = m_state.om.framebuffer->handle();
info.renderArea = renderArea;
info.clearValueCount = 0;
info.pClearValues = nullptr;
m_cmd->cmdBeginRenderPass(&info,
VK_SUBPASS_CONTENTS_INLINE);
m_cmd->trackResource(
m_state.om.framebuffer);
this->renderPassBindFramebuffer(m_state.om.framebuffer);
}
}
@ -1326,11 +1347,38 @@ namespace dxvk {
void DxvkContext::renderPassEnd() {
if (m_flags.test(DxvkContextFlag::GpRenderPassBound)) {
m_flags.clr(DxvkContextFlag::GpRenderPassBound);
m_cmd->cmdEndRenderPass();
this->renderPassUnbindFramebuffer();
}
}
void DxvkContext::renderPassBindFramebuffer(const Rc<DxvkFramebuffer>& framebuffer) {
const DxvkFramebufferSize fbSize = framebuffer->size();
VkRect2D renderArea;
renderArea.offset = VkOffset2D { 0, 0 };
renderArea.extent = VkExtent2D { fbSize.width, fbSize.height };
VkRenderPassBeginInfo info;
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
info.pNext = nullptr;
info.renderPass = framebuffer->renderPass();
info.framebuffer = framebuffer->handle();
info.renderArea = renderArea;
info.clearValueCount = 0;
info.pClearValues = nullptr;
m_cmd->cmdBeginRenderPass(&info,
VK_SUBPASS_CONTENTS_INLINE);
m_cmd->trackResource(framebuffer);
}
void DxvkContext::renderPassUnbindFramebuffer() {
m_cmd->cmdEndRenderPass();
}
void DxvkContext::updateComputePipeline() {
if (m_flags.test(DxvkContextFlag::CpDirtyPipeline)) {
m_flags.clr(DxvkContextFlag::CpDirtyPipeline);

View File

@ -188,12 +188,16 @@ namespace dxvk {
/**
* \brief Clears an active render target
*
* \param [in] attachment Attachment to clear
* \param [in] clearArea Rectangular area to clear
* \param [in] imageView Render target view to clear
* \param [in] clearArea Image area to clear
* \param [in] clearAspects Image aspects to clear
* \param [in] clearValue The clear value
*/
void clearRenderTarget(
const VkClearAttachment& attachment,
const VkClearRect& clearArea);
const Rc<DxvkImageView>& imageView,
const VkClearRect& clearRect,
VkImageAspectFlags clearAspects,
const VkClearValue& clearValue);
/**
* \brief Copies data from one buffer to another
@ -579,6 +583,10 @@ namespace dxvk {
void renderPassBegin();
void renderPassEnd();
void renderPassBindFramebuffer(
const Rc<DxvkFramebuffer>& framebuffer);
void renderPassUnbindFramebuffer();
void updateComputePipeline();
void updateComputePipelineState();

View File

@ -111,4 +111,18 @@ namespace dxvk {
m_vkd->device(), m_framebuffer, nullptr);
}
uint32_t DxvkFramebuffer::findAttachment(
const Rc<DxvkImageView>& view) const {
if (m_renderTargets.getDepthTarget().view == view)
return 0;
for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
if (m_renderTargets.getColorTarget(i).view == view)
return i;
}
return MaxNumRenderTargets;
}
}

View File

@ -185,6 +185,17 @@ namespace dxvk {
return m_renderPass->sampleCount();
}
/**
* \brief Retrieves index of a given attachment
*
* \param [in] view The image view to look up
* \returns The attachment index, or \c 0 for a depth-stencil
* attachment, or \c MaxNumRenderTargets if the given
* view is not a framebuffer attachment.
*/
uint32_t findAttachment(
const Rc<DxvkImageView>& view) const;
private:
Rc<vk::DeviceFn> m_vkd;

View File

@ -93,13 +93,6 @@ namespace dxvk::hud {
0, 1, 0, 1 });
}
VkClearAttachment clearInfo;
clearInfo.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clearInfo.colorAttachment = 0;
for (uint32_t i = 0; i < 4; i++)
clearInfo.clearValue.color.float32[i] = 0.0f;
VkClearRect clearRect;
clearRect.rect.offset = { 0, 0 };
clearRect.rect.extent = m_surfaceSize;
@ -107,7 +100,10 @@ namespace dxvk::hud {
clearRect.layerCount = 1;
m_context->bindFramebuffer(m_renderTargetFbo);
m_context->clearRenderTarget(clearInfo, clearRect);
m_context->clearRenderTarget(
m_renderTargetView, clearRect,
VK_IMAGE_ASPECT_COLOR_BIT,
VkClearValue { });
VkViewport viewport;
viewport.x = 0.0f;