Further steps towards Vulkan framebuffer support

This commit is contained in:
Henrik Rydgård 2017-05-16 17:20:22 +02:00
parent 6bcfe539f7
commit a7dd6d6085
10 changed files with 278 additions and 107 deletions

View file

@ -258,6 +258,9 @@ public:
VkCommandBuffer GetInitCommandBuffer();
VkFramebuffer GetSurfaceFramebuffer() {
return framebuffers_[current_buffer];
}
// This must only be accessed between BeginSurfaceRenderPass and EndSurfaceRenderPass.
VkCommandBuffer GetSurfaceCommandBuffer() {
return frame_[curFrame_ & 1].cmdBuf;

View file

@ -825,7 +825,9 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
if (displayFramebufPtr_ == 0) {
DEBUG_LOG(FRAMEBUF, "Display disabled, displaying only black");
// No framebuffer to display! Clear to black.
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
if (useBufferedRendering_) {
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
}
return;
}

View file

@ -21,7 +21,8 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{457F45D2-556F-47BC-A31D-AFF0D15BEAED}</ProjectGuid>
<RootNamespace>GPU</RootNamespace>
<WindowsTargetPlatformVersion></WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>
</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">

View file

@ -408,9 +408,6 @@
<ClCompile Include="Vulkan\FragmentShaderGeneratorVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\FramebufferVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
<ClCompile Include="Vulkan\GPU_Vulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
@ -510,5 +507,8 @@
<ClCompile Include="Common\ShaderCommon.cpp">
<Filter>Common</Filter>
</ClCompile>
<ClCompile Include="Vulkan\FramebufferVulkan.cpp">
<Filter>Vulkan</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -883,6 +883,7 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
// Note: we won't get here if the clear is alpha but not color, or color but not alpha.
// We let the framebuffer manager handle the clear. It can use renderpasses to optimize on tilers.
// If non-buffered though, it'll just do a plain clear.
framebufferManager_->NotifyClear(gstate.isClearModeColorMask(), gstate.isClearModeAlphaMask(), gstate.isClearModeDepthMask(), result.color, result.depth);
int scissorX1 = gstate.getScissorX1();

View file

@ -52,8 +52,6 @@
#include "GPU/Vulkan/ShaderManagerVulkan.h"
#include "GPU/Vulkan/VulkanUtil.h"
const VkFormat framebufFormat = VK_FORMAT_B8G8R8A8_UNORM;
static const char tex_fs[] = R"(#version 400
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
@ -92,7 +90,8 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk
pixelBufObj_(nullptr),
currentPBO_(0),
curFrame_(0),
pipelineBasicTex_(VK_NULL_HANDLE),
pipelineBasicTexBackBuffer_(VK_NULL_HANDLE),
pipelineBasicTexFrameBuffer_(VK_NULL_HANDLE),
pipelinePostShader_(VK_NULL_HANDLE),
vulkan2D_(vulkan) {
@ -117,67 +116,6 @@ void FramebufferManagerVulkan::SetShaderManager(ShaderManagerVulkan *sm) {
}
void FramebufferManagerVulkan::InitDeviceObjects() {
// Create a bunch of render pass objects, for normal rendering with a depth buffer,
// with and without pre-clearing of both depth/stencil and color, so 4 combos.
VkAttachmentDescription attachments[2] = {};
attachments[0].format = framebufFormat;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].flags = 0;
attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].flags = 0;
VkAttachmentReference color_reference = {};
color_reference.attachment = 0;
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_reference = {};
depth_reference.attachment = 1;
depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.flags = 0;
subpass.inputAttachmentCount = 0;
subpass.pInputAttachments = NULL;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
subpass.pResolveAttachments = NULL;
subpass.pDepthStencilAttachment = &depth_reference;
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = NULL;
VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rp.attachmentCount = 2;
rp.pAttachments = attachments;
rp.subpassCount = 1;
rp.pSubpasses = &subpass;
rp.dependencyCount = 0;
rp.pDependencies = NULL;
// TODO: Maybe LOAD_OP_DONT_CARE makes sense in some situations. Additionally,
// there is often no need to store the depth buffer afterwards, although hard to know up front.
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpLoadColorLoadDepth_);
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpClearColorLoadDepth_);
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpClearColorClearDepth_);
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &rpLoadColorClearDepth_);
// Initialize framedata
for (int i = 0; i < 2; i++) {
VkCommandPoolCreateInfo cp = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
@ -196,7 +134,9 @@ void FramebufferManagerVulkan::InitDeviceObjects() {
assert(fsBasicTex_ != VK_NULL_HANDLE);
assert(vsBasicTex_ != VK_NULL_HANDLE);
pipelineBasicTex_ = vulkan2D_.GetPipeline(pipelineCache2D_, rpClearColorClearDepth_, vsBasicTex_, fsBasicTex_);
// Get a representative render pass and use when creating the pipeline.
pipelineBasicTexBackBuffer_ = vulkan2D_.GetPipeline(pipelineCache2D_, vulkan_->GetSurfaceRenderPass(), vsBasicTex_, fsBasicTex_);
pipelineBasicTexFrameBuffer_ = vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS), vsBasicTex_, fsBasicTex_);
VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
@ -213,15 +153,6 @@ void FramebufferManagerVulkan::InitDeviceObjects() {
}
void FramebufferManagerVulkan::DestroyDeviceObjects() {
if (rpLoadColorLoadDepth_ != VK_NULL_HANDLE)
vulkan_->Delete().QueueDeleteRenderPass(rpLoadColorLoadDepth_);
if (rpClearColorLoadDepth_ != VK_NULL_HANDLE)
vulkan_->Delete().QueueDeleteRenderPass(rpClearColorLoadDepth_);
if (rpClearColorClearDepth_ != VK_NULL_HANDLE)
vulkan_->Delete().QueueDeleteRenderPass(rpClearColorClearDepth_);
if (rpLoadColorClearDepth_ != VK_NULL_HANDLE)
vulkan_->Delete().QueueDeleteRenderPass(rpLoadColorClearDepth_);
for (int i = 0; i < 2; i++) {
if (frameData_[i].numCommandBuffers_ > 0) {
vkFreeCommandBuffers(vulkan_->GetDevice(), frameData_[i].cmdPool_, frameData_[i].numCommandBuffers_, frameData_[i].commandBuffers_);

View file

@ -188,18 +188,14 @@ private:
// This gets copied to the current frame's push buffer as needed.
PostShaderUniforms postUniforms_;
// Renderpasses, all combination of preserving or clearing fb contents
VkRenderPass rpLoadColorLoadDepth_;
VkRenderPass rpClearColorLoadDepth_;
VkRenderPass rpLoadColorClearDepth_;
VkRenderPass rpClearColorClearDepth_;
VkPipelineCache pipelineCache2D_;
// Basic shaders
VkShaderModule fsBasicTex_;
VkShaderModule vsBasicTex_;
VkPipeline pipelineBasicTex_;
// Might need different pipelines for rendering to backbuffer vs framebuffers due to color format incompatibility
VkPipeline pipelineBasicTexBackBuffer_;
VkPipeline pipelineBasicTexFrameBuffer_;
// Postprocessing
VkPipeline pipelinePostShader_;

View file

@ -321,6 +321,7 @@ enum class NativeObject {
BACKBUFFER_COLOR_TEX,
BACKBUFFER_DEPTH_TEX,
FEATURE_LEVEL,
RENDERPASS,
};
enum FBColorDepth {

View file

@ -411,7 +411,12 @@ public:
std::vector<std::string> GetFeatureList() const override;
uintptr_t GetNativeObject(NativeObject obj) const override {
return 0;
switch (obj) {
case NativeObject::RENDERPASS:
return (uintptr_t)renderPasses_[0];
default:
return 0;
}
}
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override;
@ -432,6 +437,13 @@ private:
VkPipelineLayout pipelineLayout_;
VkPipelineCache pipelineCache_;
inline int RPIndex(RPAction color, RPAction depth) {
return (int)depth * 3 + (int)color;
}
// Renderpasses, all combination of preserving or clearing or dont-care-ing fb contents.
VkRenderPass renderPasses_[9];
VkCommandPool cmdPool_;
VkDevice device_;
VkQueue queue_;
@ -464,6 +476,9 @@ private:
VulkanPushBuffer *push_ = nullptr;
DeviceCaps caps_{};
VkFramebuffer curFramebuffer_ = VK_NULL_HANDLE;;
VkRenderPass curRenderPass_ = VK_NULL_HANDLE;
};
static int GetBpp(VkFormat format) {
@ -714,9 +729,79 @@ VKContext::VKContext(VulkanContext *vulkan)
assert(VK_SUCCESS == res);
pipelineCache_ = vulkan_->CreatePipelineCache();
// Create a bunch of render pass objects, for normal rendering with a depth buffer,
// with and without pre-clearing of both depth/stencil and color, so 4 combos.
VkAttachmentDescription attachments[2] = {};
attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].flags = 0;
attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].flags = 0;
VkAttachmentReference color_reference = {};
color_reference.attachment = 0;
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_reference = {};
depth_reference.attachment = 1;
depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.flags = 0;
subpass.inputAttachmentCount = 0;
subpass.pInputAttachments = NULL;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
subpass.pResolveAttachments = NULL;
subpass.pDepthStencilAttachment = &depth_reference;
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = NULL;
VkRenderPassCreateInfo rp = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rp.attachmentCount = 2;
rp.pAttachments = attachments;
rp.subpassCount = 1;
rp.pSubpasses = &subpass;
rp.dependencyCount = 0;
rp.pDependencies = NULL;
for (int depth = 0; depth < 3; depth++) {
switch ((RPAction)depth) {
case RPAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break;
case RPAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break;
case RPAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break;
}
for (int color = 0; color < 3; color++) {
switch ((RPAction)color) {
case RPAction::CLEAR: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; break;
case RPAction::KEEP: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; break;
case RPAction::DONT_CARE: attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; break;
}
vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &renderPasses_[RPIndex((RPAction)color, (RPAction)depth)]);
}
}
}
VKContext::~VKContext() {
for (int i = 0; i < 9; i++) {
vulkan_->Delete().QueueDeleteRenderPass(renderPasses_[i]);
}
vulkan_->Delete().QueueDeleteCommandPool(cmdPool_);
// This also destroys all descriptor sets.
for (int i = 0; i < 2; i++) {
@ -748,26 +833,14 @@ void VKContext::BeginFrame() {
scissor_.extent.height = pixel_yres;
scissorDirty_ = true;
viewportDirty_ = true;
int colorval = 0xFF000000;
float depthVal = 0.0;
int stencilVal = 0;
VkClearValue clearVal[2] = {};
Uint8x4ToFloat4(colorval, clearVal[0].color.float32);
// // Debug flicker - used to see if we swap at all. no longer necessary
// if (frameNum_ & 1)
// clearVal[0].color.float32[2] = 1.0f;
clearVal[1].depthStencil.depth = depthVal;
clearVal[1].depthStencil.stencil = stencilVal;
vulkan_->BeginSurfaceRenderPass(clearVal);
}
void VKContext::EndFrame() {
vulkan_->EndSurfaceRenderPass();
if (curRenderPass_) {
vulkan_->EndSurfaceRenderPass();
curRenderPass_ = VK_NULL_HANDLE;
curFramebuffer_ = VK_NULL_HANDLE;
}
// Stop collecting data in the frame's data pushbuffer.
push_->End();
@ -1277,18 +1350,124 @@ uint32_t VKContext::GetDataFormatSupport(DataFormat fmt) const {
}
}
// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine
// to let them have individual non-pooled allocations.
struct VKImage {
VkImage image;
VkImageView view;
VkDeviceMemory memory;
VkImageLayout layout;
};
void CreateImage(VulkanContext *vulkan, VKImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color) {
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.arrayLayers = 1;
ici.mipLevels = 1;
ici.extent.width = width;
ici.extent.height = height;
ici.extent.depth = 1;
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ici.imageType = VK_IMAGE_TYPE_2D;
ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.format = format;
ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
if (color) {
ici.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
} else {
ici.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
vkCreateImage(vulkan->GetDevice(), &ici, nullptr, &img.image);
VkMemoryRequirements memreq;
vkGetImageMemoryRequirements(vulkan->GetDevice(), img.image, &memreq);
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
alloc.allocationSize = memreq.size;
vulkan->MemoryTypeFromProperties(memreq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &alloc.memoryTypeIndex);
VkResult res = vkAllocateMemory(vulkan->GetDevice(), &alloc, nullptr, &img.memory);
assert(res);
res = vkBindImageMemory(vulkan->GetDevice(), img.image, img.memory, 0);
assert(res);
img.layout = initialLayout;
VkImageViewCreateInfo ivci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
ivci.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
ivci.format = ici.format;
ivci.image = img.image;
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.subresourceRange.aspectMask = color ? VK_IMAGE_ASPECT_COLOR_BIT : (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
ivci.subresourceRange.layerCount = 1;
ivci.subresourceRange.levelCount = 1;
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.view);
assert(res);
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
barrier.image = img.image;
barrier.srcAccessMask = 0;
switch (initialLayout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
}
barrier.newLayout = initialLayout;
barrier.subresourceRange.aspectMask = ivci.subresourceRange.aspectMask;
vkCmdPipelineBarrier(vulkan->GetInitCommandBuffer(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, 0, 0, 0, 1, &barrier);
}
// A VKFramebuffer is a VkFramebuffer plus all the textures it owns.
class VKFramebuffer : public Framebuffer {
public:
VKFramebuffer(VulkanContext *vk) : vulkan_(vk) {}
~VKFramebuffer() {
vulkan_->Delete().QueueDeleteImage(color.image);
vulkan_->Delete().QueueDeleteImage(depth.image);
vulkan_->Delete().QueueDeleteImageView(color.view);
vulkan_->Delete().QueueDeleteImageView(depth.view);
vulkan_->Delete().QueueDeleteDeviceMemory(color.memory);
vulkan_->Delete().QueueDeleteDeviceMemory(depth.memory);
vulkan_->Delete().QueueDeleteFramebuffer(framebuf);
}
VkFramebuffer framebuf;
VKImage color;
VKImage depth;
int width;
int height;
private:
VulkanContext *vulkan_;
};
Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) {
VKFramebuffer *fb = new VKFramebuffer();
VKFramebuffer *fb = new VKFramebuffer(vulkan_);
fb->width = desc.width;
fb->height = desc.height;
CreateImage(vulkan_, fb->color, fb->width, fb->height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true);
CreateImage(vulkan_, fb->depth, fb->width, fb->height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false);
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
VkImageView views[2]{};
fbci.renderPass = renderPasses_[0];
fbci.attachmentCount = 2;
fbci.pAttachments = views;
views[0] = fb->color.view;
views[1] = fb->depth.view;
fbci.width = fb->width;
fbci.height = fb->height;
fbci.layers = 1;
vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &fb->framebuf);
return fb;
}
@ -1305,11 +1484,67 @@ bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int sr
// These functions should be self explanatory.
void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) {
VkFramebuffer framebuf;
int w;
int h;
if (fbo) {
VKFramebuffer *fb = (VKFramebuffer *)fbo;
framebuf = fb->framebuf;
w = fb->width;
h = fb->height;
} else {
framebuf = vulkan_->GetSurfaceFramebuffer();
w = vulkan_->GetWidth();
h = vulkan_->GetHeight();
}
VkCommandBuffer cmd = vulkan_->GetSurfaceCommandBuffer();
if (framebuf == curFramebuffer_) {
// If we're asking to clear, but already bound, we'll just keep it bound but send a clear command.
// We will try to avoid this as much as possible. Also, TODO, do a single vkCmdClearAttachments to clear both.
if (rp.color == RPAction::CLEAR) {
VkClearAttachment clear{};
clear.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
Uint8x4ToFloat4(rp.clearColor, clear.clearValue.color.float32);
clear.colorAttachment = 0;
VkClearRect rc{ {0,0,(uint32_t)w,(uint32_t)h}, 0, 1 };
vkCmdClearAttachments(cmd, 1, &clear, 1, &rc);
}
if (rp.depth == RPAction::CLEAR) {
VkClearAttachment clear{};
clear.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
clear.clearValue.depthStencil.depth = rp.clearDepth;
clear.clearValue.depthStencil.stencil = rp.clearStencil;
clear.colorAttachment = 0;
VkClearRect rc{ { 0,0,w,h }, 0, 1 };
vkCmdClearAttachments(cmd, 1, &clear, 1, &rc);
}
// We're done.
return;
}
// OK, we're switching framebuffers.
if (curRenderPass_ != VK_NULL_HANDLE) {
vkCmdEndRenderPass(cmd);
}
VkClearValue clearVal[2] = {};
Uint8x4ToFloat4(rp.clearColor, clearVal[0].color.float32);
clearVal[1].depthStencil.depth = rp.clearDepth;
clearVal[1].depthStencil.stencil = rp.clearStencil;
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
rp_begin.renderPass = fbo ? renderPasses_[RPIndex(rp.color, rp.depth)] : vulkan_->GetSurfaceRenderPass();
rp_begin.framebuffer = framebuf;
rp_begin.renderArea.offset.x = 0;
rp_begin.renderArea.offset.y = 0;
rp_begin.renderArea.extent.width = w;
rp_begin.renderArea.extent.height = h;
rp_begin.clearValueCount = 2;
rp_begin.pClearValues = clearVal;
vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
curFramebuffer_ = framebuf;
curRenderPass_ = rp_begin.renderPass;
}
// color must be 0, for now.
@ -1317,6 +1552,7 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne
VKFramebuffer *fb = (VKFramebuffer *)fbo;
}
void VKContext::BindFramebufferForRead(Framebuffer *fbo) { /* noop */ }
uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) {

@ -1 +1 @@
Subproject commit e18cface3db64ccb96738dc128fe769b28fff65c
Subproject commit d02ba7407050f445edf9e908374ad4bf3b2f237b