More multiview work

This commit is contained in:
Henrik Rydgård 2022-10-18 00:26:10 +02:00
parent d3804ec2e5
commit fb250c4b29
38 changed files with 302 additions and 157 deletions

View file

@ -94,11 +94,8 @@ public:
bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) override;
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
Framebuffer *GetCurrentRenderTarget() override {
return curRenderTarget_;
}
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit) override;
void BindFramebufferAsRenderTarget(Framebuffer *fbo, int layer, const RenderPassInfo &rp, const char *tag) override;
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
@ -1658,7 +1655,8 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel
return true;
}
void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) {
void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, int layer, const RenderPassInfo &rp, const char *tag) {
_dbg_assert_(layer == ALL_LAYERS || layer == 0); // No multiple layer support on D3D
// TODO: deviceContext1 can actually discard. Useful on Windows Mobile.
if (fbo) {
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
@ -1704,8 +1702,9 @@ void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Ren
stepId_++;
}
void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit) {
_assert_(binding < MAX_BOUND_TEXTURES);
void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {
_dbg_assert_(binding < MAX_BOUND_TEXTURES);
_dbg_assert_(layer == ALL_LAYERS || layer == 0); // No multiple layer support on D3D
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
switch (channelBit) {
case FBChannel::FB_COLOR_BIT:

View file

@ -531,12 +531,9 @@ public:
bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) override;
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
Framebuffer *GetCurrentRenderTarget() override {
return curRenderTarget_;
}
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit) override;
void BindFramebufferAsRenderTarget(Framebuffer *fbo, int layer, const RenderPassInfo &rp, const char *tag) override;
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override;
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
@ -1292,7 +1289,8 @@ D3D9Framebuffer::~D3D9Framebuffer() {
}
}
void D3D9Context::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) {
void D3D9Context::BindFramebufferAsRenderTarget(Framebuffer *fbo, int layer, const RenderPassInfo &rp, const char *tag) {
_dbg_assert_(layer == ALL_LAYERS || layer == 0); // No stereo support
if (fbo) {
D3D9Framebuffer *fb = (D3D9Framebuffer *)fbo;
device_->SetRenderTarget(0, fb->surf);
@ -1351,8 +1349,9 @@ uintptr_t D3D9Context::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit
}
}
void D3D9Context::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit) {
_assert_(binding < MAX_BOUND_TEXTURES);
void D3D9Context::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {
_dbg_assert_(binding < MAX_BOUND_TEXTURES);
_dbg_assert_(layer == ALL_LAYERS || layer == 0); // No stereo support
D3D9Framebuffer *fb = (D3D9Framebuffer *)fbo;
switch (channelBit) {
case FB_DEPTH_BIT:

View file

@ -354,11 +354,8 @@ public:
bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) override;
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
Framebuffer *GetCurrentRenderTarget() override {
return curRenderTarget_;
}
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit) override;
void BindFramebufferAsRenderTarget(Framebuffer *fbo, int layer, const RenderPassInfo &rp, const char *tag) override;
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
@ -1398,7 +1395,9 @@ Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) {
return fbo;
}
void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) {
void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, int layer, const RenderPassInfo &rp, const char *tag) {
_dbg_assert_(layer == ALL_LAYERS || layer == 0);
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
GLRRenderPassAction color = (GLRRenderPassAction)rp.color;
GLRRenderPassAction depth = (GLRRenderPassAction)rp.depth;
@ -1439,7 +1438,7 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in
return true;
}
void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit) {
void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
_assert_(binding < MAX_TEXTURE_SLOTS);

View file

@ -608,8 +608,7 @@ void VulkanContext::ChooseDevice(int physical_device) {
deviceFeatures_.enabled.standard.geometryShader = deviceFeatures_.available.standard.geometryShader;
deviceFeatures_.enabled.multiview = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES };
// Don't yet enable these.
// deviceFeatures_.enabled.multiview.multiview = deviceFeatures_.available.multiview.multiview;
deviceFeatures_.enabled.multiview.multiview = deviceFeatures_.available.multiview.multiview;
// deviceFeatures_.enabled.multiview.multiviewGeometryShader = deviceFeatures_.available.multiview.multiviewGeometryShader;
GetDeviceLayerExtensionList(nullptr, device_extension_properties_);
@ -1246,7 +1245,7 @@ bool VulkanContext::CreateShaderModule(const std::vector<uint32_t> &spirv, VkSha
}
}
void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int numMipLevels, VkImageAspectFlags aspectMask,
void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int numMipLevels, int numLayers, VkImageAspectFlags aspectMask,
VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) {
@ -1259,7 +1258,7 @@ void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int
image_memory_barrier.subresourceRange.aspectMask = aspectMask;
image_memory_barrier.subresourceRange.baseMipLevel = baseMip;
image_memory_barrier.subresourceRange.levelCount = numMipLevels;
image_memory_barrier.subresourceRange.layerCount = 1; // We never use more than one layer, and old Mali drivers have problems with VK_REMAINING_ARRAY_LAYERS/VK_REMAINING_MIP_LEVELS.
image_memory_barrier.subresourceRange.layerCount = numLayers; // We never use more than one layer, and old Mali drivers have problems with VK_REMAINING_ARRAY_LAYERS/VK_REMAINING_MIP_LEVELS.
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
vkCmdPipelineBarrier(cmd, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);

View file

@ -441,7 +441,7 @@ private:
};
// Detailed control.
void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int mipLevels, VkImageAspectFlags aspectMask,
void TransitionImageLayout2(VkCommandBuffer cmd, VkImage image, int baseMip, int mipLevels, int numLayers, VkImageAspectFlags aspectMask,
VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask);

View file

@ -87,7 +87,7 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int depth, i
switch (initialLayout) {
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
TransitionImageLayout2(cmd, image_, 0, numMips, VK_IMAGE_ASPECT_COLOR_BIT,
TransitionImageLayout2(cmd, image_, 0, numMips, 1, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, initialLayout,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, VK_ACCESS_TRANSFER_WRITE_BIT);
@ -164,7 +164,7 @@ void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bo
_assert_msg_(firstMipToGenerate < numMips_, "Can't generate levels beyond storage");
// Transition the pre-set levels to GENERAL.
TransitionImageLayout2(cmd, image_, 0, firstMipToGenerate, VK_IMAGE_ASPECT_COLOR_BIT,
TransitionImageLayout2(cmd, image_, 0, firstMipToGenerate, 1, VK_IMAGE_ASPECT_COLOR_BIT,
fromCompute ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_GENERAL,
fromCompute ? VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT : VK_PIPELINE_STAGE_TRANSFER_BIT,
@ -173,7 +173,7 @@ void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bo
VK_ACCESS_TRANSFER_READ_BIT);
// Do the same with the uninitialized levels.
TransitionImageLayout2(cmd, image_, firstMipToGenerate, numMips_ - firstMipToGenerate,
TransitionImageLayout2(cmd, image_, firstMipToGenerate, numMips_ - firstMipToGenerate, 1,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_GENERAL,
@ -206,7 +206,7 @@ void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bo
vkCmdBlitImage(cmd, image_, VK_IMAGE_LAYOUT_GENERAL, image_, VK_IMAGE_LAYOUT_GENERAL, 1, &blit, VK_FILTER_LINEAR);
TransitionImageLayout2(cmd, image_, mip, 1, VK_IMAGE_ASPECT_COLOR_BIT,
TransitionImageLayout2(cmd, image_, mip, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
@ -214,7 +214,7 @@ void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bo
}
void VulkanTexture::EndCreate(VkCommandBuffer cmd, bool vertexTexture, VkPipelineStageFlags prevStage, VkImageLayout layout) {
TransitionImageLayout2(cmd, image_, 0, numMips_,
TransitionImageLayout2(cmd, image_, 0, numMips_, 1,
VK_IMAGE_ASPECT_COLOR_BIT,
layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
prevStage, vertexTexture ? VK_PIPELINE_STAGE_VERTEX_SHADER_BIT : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,

View file

@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <functional>
#include <vector>
@ -121,6 +122,15 @@ public:
return writePtr_ + off;
}
template<class T>
void PushUBOData(const T &data, VkDescriptorBufferInfo *info) {
uint32_t bindOffset;
void *ptr = PushAligned(sizeof(T), &bindOffset, &info->buffer, vulkan_->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment);
memcpy(ptr, &data, sizeof(T));
info->offset = bindOffset;
info->range = sizeof(T);
}
size_t GetTotalSize() const;
private:

View file

@ -249,7 +249,7 @@ bool VulkanQueueRunner::InitDepthStencilBuffer(VkCommandBuffer cmd) {
vulkan_->SetDebugName(depth_.image, VK_OBJECT_TYPE_IMAGE, "BackbufferDepth");
TransitionImageLayout2(cmd, depth_.image, 0, 1,
TransitionImageLayout2(cmd, depth_.image, 0, 1, 1,
aspectMask,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
@ -331,6 +331,10 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas
bool hasDepth = RenderPassTypeHasDepth(rpType);
bool multiview = RenderPassTypeHasMultiView(rpType);
if (multiview) {
// TODO: Assert that the device has multiview support enabled.
}
VkAttachmentDescription attachments[2] = {};
attachments[0].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
@ -905,6 +909,10 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
case RP_TYPE_COLOR_DEPTH: renderCmd = "RENDER_DEPTH"; break;
case RP_TYPE_COLOR_INPUT: renderCmd = "RENDER_INPUT"; break;
case RP_TYPE_COLOR_DEPTH_INPUT: renderCmd = "RENDER_DEPTH_INPUT"; break;
case RP_TYPE_MULTIVIEW_COLOR: renderCmd = "MV_RENDER"; break;
case RP_TYPE_MULTIVIEW_COLOR_DEPTH: renderCmd = "MV_RENDER_DEPTH"; break;
case RP_TYPE_MULTIVIEW_COLOR_INPUT: renderCmd = "MV_RENDER_INPUT"; break;
case RP_TYPE_MULTIVIEW_COLOR_DEPTH_INPUT: renderCmd = "MV_RENDER_DEPTH_INPUT"; break;
default: renderCmd = "N/A";
}
snprintf(buffer, sizeof(buffer), "%s %s %s (draws: %d, %dx%d/%dx%d)", renderCmd, step.tag, step.render.framebuffer ? step.render.framebuffer->Tag() : "", step.render.numDraws, actual_w, actual_h, w, h);
@ -1565,6 +1573,10 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
vkCmdDraw(cmd, c.draw.count, 1, c.draw.offset, 0);
break;
case VKRRenderCommand::BIND_DESCRIPTOR_SET:
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, c.bindDescSet.setIndex, 1, &c.bindDescSet.descSet, 0, nullptr);
break;
case VKRRenderCommand::CLEAR:
{
// If we get here, we failed to merge a clear into a render pass load op. This is bad for perf.
@ -1643,7 +1655,8 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
renderPass = GetRenderPass(key);
VKRFramebuffer *fb = step.render.framebuffer;
framebuf = fb->Get(renderPass, step.render.renderPassType);
framebuf = fb->Get(renderPass, step.render.renderPassType, step.render.layer);
_dbg_assert_(framebuf != VK_NULL_HANDLE);
w = fb->width;
h = fb->height;
@ -2014,7 +2027,7 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
if (step.readback.src == nullptr) {
// We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT,
// and then back into it.
TransitionImageLayout2(cmd, backbufferImage_, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT,
TransitionImageLayout2(cmd, backbufferImage_, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, VK_ACCESS_TRANSFER_READ_BIT);
@ -2048,7 +2061,7 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
if (step.readback.src == nullptr) {
// We only take screenshots after the main render pass (anything else would be stupid) so we need to transition out of PRESENT,
// and then back into it.
TransitionImageLayout2(cmd, backbufferImage_, 0, 1, VK_IMAGE_ASPECT_COLOR_BIT,
TransitionImageLayout2(cmd, backbufferImage_, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_ACCESS_TRANSFER_READ_BIT, 0);
@ -2079,7 +2092,7 @@ void VulkanQueueRunner::PerformReadbackImage(const VKRStep &step, VkCommandBuffe
vkCmdCopyImageToBuffer(cmd, step.readback_image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, readbackBuffer_, 1, &region);
// Now transfer it back to a texture.
TransitionImageLayout2(cmd, step.readback_image.image, 0, 1,
TransitionImageLayout2(cmd, step.readback_image.image, 0, 1, 1, // TODO: Handle multiple layers
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,

View file

@ -37,12 +37,13 @@ enum class VKRRenderCommand : uint8_t {
DRAW,
DRAW_INDEXED,
PUSH_CONSTANTS,
BIND_DESCRIPTOR_SET,
SELF_DEPENDENCY_BARRIER,
DEBUG_ANNOTATION,
NUM_RENDER_COMMANDS,
};
enum class PipelineFlags {
enum class PipelineFlags : u8 {
NONE = 0,
USES_BLEND_CONSTANT = (1 << 1),
USES_DEPTH_STENCIL = (1 << 2), // Reads or writes the depth or stencil buffers.
@ -115,7 +116,7 @@ struct VkRenderData {
VkDescriptorSet ds;
int numUboOffsets;
uint32_t uboOffsets[3];
VkBuffer vbuffer; // might need to increase at some point
VkBuffer vbuffer;
VkBuffer ibuffer;
uint32_t voffset;
uint32_t ioffset;
@ -152,6 +153,10 @@ struct VkRenderData {
struct {
const char *annotation;
} debugAnnotation;
struct {
uint32_t setIndex;
VkDescriptorSet descSet;
} bindDescSet;
};
};
@ -207,6 +212,7 @@ struct VKRStep {
VKRRenderPassStoreAction depthStore;
VKRRenderPassStoreAction stencilStore;
u8 clearStencil;
s8 layer;
uint32_t clearColor;
float clearDepth;
int numDraws;

View file

@ -182,43 +182,51 @@ void VKRFramebuffer::UpdateTag(const char *newTag) {
vulkan_->SetDebugName(depth.imageView, VK_OBJECT_TYPE_IMAGE_VIEW, name);
}
for (int rpType = 0; rpType < RP_TYPE_COUNT; rpType++) {
if (framebuf[rpType]) {
snprintf(name, sizeof(name), "fb_%s", tag_.c_str());
vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, name);
for (int i = 0; i < 2; i++) {
if (framebuf[rpType][i]) {
snprintf(name, sizeof(name), "fb_%s_%d", tag_.c_str(), i);
vulkan_->SetDebugName(framebuf[(int)rpType][i], VK_OBJECT_TYPE_FRAMEBUFFER, name);
}
}
}
}
VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType) {
if (framebuf[(int)rpType]) {
return framebuf[(int)rpType];
VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType, int layer) {
bool multiview = RenderPassTypeHasMultiView(rpType);
if (RenderPassTypeHasMultiView(rpType)) {
_dbg_assert_(layer == -1);
layer = 0;
}
if (framebuf[(int)rpType][layer]) {
return framebuf[(int)rpType][layer];
}
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
VkImageView views[2]{};
bool hasDepth = rpType == RP_TYPE_BACKBUFFER || rpType == RP_TYPE_COLOR_DEPTH || rpType == RP_TYPE_COLOR_DEPTH_INPUT;
views[0] = color.imageView;
bool hasDepth = RenderPassTypeHasDepth(rpType);
views[0] = (multiview || numLayers == 0) ? color.imageView : color.layerViews[layer];
if (hasDepth) {
_dbg_assert_(depth.imageView != VK_NULL_HANDLE);
views[1] = depth.imageView;
views[1] = (multiview || numLayers == 0) ? depth.imageView : depth.layerViews[layer];
}
fbci.renderPass = compatibleRenderPass->Get(vulkan_, rpType);
fbci.attachmentCount = hasDepth ? 2 : 1;
fbci.pAttachments = views;
fbci.width = width;
fbci.height = height;
fbci.layers = 1;
fbci.layers = 1; // With multiview, this should be set as 1.
VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)rpType]);
VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)rpType][layer]);
_assert_(res == VK_SUCCESS);
if (!tag_.empty() && vulkan_->Extensions().EXT_debug_utils) {
vulkan_->SetDebugName(framebuf[(int)rpType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str());
vulkan_->SetDebugName(framebuf[(int)rpType][layer], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s_%d", tag_.c_str(), layer).c_str());
}
return framebuf[(int)rpType];
return framebuf[(int)rpType][layer];
}
VKRFramebuffer::~VKRFramebuffer() {
@ -236,13 +244,27 @@ VKRFramebuffer::~VKRFramebuffer() {
}
if (depth.depthSampleView)
vulkan_->Delete().QueueDeleteImageView(depth.depthSampleView);
for (int i = 0; i < 2; i++) {
if (color.layerViews[i]) {
vulkan_->Delete().QueueDeleteImageView(color.layerViews[i]);
}
if (depth.layerViews[i]) {
vulkan_->Delete().QueueDeleteImageView(depth.layerViews[i]);
}
}
for (auto &fb : framebuf) {
if (fb)
vulkan_->Delete().QueueDeleteFramebuffer(fb);
for (int i = 0; i < 2; i++) {
if (fb[i]) {
vulkan_->Delete().QueueDeleteFramebuffer(fb[i]);
}
}
}
}
void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, int numLayers, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag) {
// We don't support more exotic layer setups for now. Mono or stereo.
_dbg_assert_(numLayers == 1 || numLayers == 2);
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.arrayLayers = numLayers;
ici.mipLevels = 1;
@ -275,11 +297,12 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int
ivci.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
ivci.format = ici.format;
ivci.image = img.image;
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.viewType = numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY;
ivci.subresourceRange.aspectMask = aspects;
ivci.subresourceRange.layerCount = 1;
ivci.subresourceRange.layerCount = numLayers;
ivci.subresourceRange.levelCount = 1;
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.imageView);
_dbg_assert_(res == VK_SUCCESS);
// Separate view for texture sampling that only exposes depth.
@ -291,6 +314,18 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int
img.depthSampleView = VK_NULL_HANDLE;
}
// Create 2D views for both layers, if layered.
// Useful when multipassing shaders that don't yet exist in a single-pass-stereo version.
if (numLayers == 2) {
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
ivci.subresourceRange.layerCount = 1;
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.layerViews[0]);
_dbg_assert_(res == VK_SUCCESS);
ivci.subresourceRange.baseArrayLayer = 1;
res = vkCreateImageView(vulkan->GetDevice(), &ivci, nullptr, &img.layerViews[1]);
_dbg_assert_(res == VK_SUCCESS);
}
VkPipelineStageFlags dstStage;
VkAccessFlagBits dstAccessMask;
switch (initialLayout) {
@ -311,12 +346,11 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int
return;
}
TransitionImageLayout2(cmd, img.image, 0, 1, aspects,
TransitionImageLayout2(cmd, img.image, 0, 1, numLayers, aspects,
VK_IMAGE_LAYOUT_UNDEFINED, initialLayout,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dstStage,
0, dstAccessMask);
img.layout = initialLayout;
img.format = format;
img.tag = tag ? tag : "N/A";
}
@ -710,10 +744,18 @@ void VulkanRenderManager::EndCurRenderStep() {
RenderPassType rpType = depthStencil ? RP_TYPE_COLOR_DEPTH : RP_TYPE_COLOR;
if (!curRenderStep_->render.framebuffer) {
rpType = RP_TYPE_BACKBUFFER;
} else if (curPipelineFlags_ & PipelineFlags::USES_INPUT_ATTACHMENT) {
// Not allowed on backbuffers.
rpType = depthStencil ? RP_TYPE_COLOR_DEPTH_INPUT : RP_TYPE_COLOR_INPUT;
} else {
if (curPipelineFlags_ & PipelineFlags::USES_INPUT_ATTACHMENT) {
// Not allowed on backbuffers.
rpType = depthStencil ? RP_TYPE_COLOR_DEPTH_INPUT : RP_TYPE_COLOR_INPUT;
}
// Framebuffers can be stereo, and if so, will control the render pass type to match.
// Pipelines can be mono and render fine to stereo etc, so not checking them here.
if (curRenderStep_->render.framebuffer->numLayers > 1) {
rpType = (RenderPassType)(rpType | RP_TYPE_MULTIVIEW_COLOR);
}
}
// TODO: Also add render pass types for depth/stencil-less.
VKRRenderPass *renderPass = queueRunner_.GetRenderPass(key);
@ -752,7 +794,7 @@ void VulkanRenderManager::BindCurrentFramebufferAsInputAttachment0(VkImageAspect
curRenderStep_->commands.push_back(VkRenderData{ VKRRenderCommand::SELF_DEPENDENCY_BARRIER });
}
void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) {
void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, int layer, const char *tag) {
_dbg_assert_(insideFrame_);
// Eliminate dupes (bind of the framebuffer we already are rendering to), instantly convert to a clear if possible.
if (!steps_.empty() && steps_.back()->stepType == VKRStepType::RENDER && steps_.back()->render.framebuffer == fb) {
@ -833,6 +875,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
VKRStep *step = new VKRStep{ VKRStepType::RENDER };
step->render.framebuffer = fb;
step->render.layer = layer;
step->render.colorLoad = color;
step->render.depthLoad = depth;
step->render.stencilLoad = stencil;
@ -1175,7 +1218,7 @@ void VulkanRenderManager::BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect,
steps_.push_back(step);
}
VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit) {
VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBit, int layer) {
_dbg_assert_(curRenderStep_ != nullptr);
// We don't support texturing from stencil, neither do we support texturing from depth|stencil together (nonsensical).
@ -1210,7 +1253,12 @@ VkImageView VulkanRenderManager::BindFramebufferAsTexture(VKRFramebuffer *fb, in
// Add this pretransition unless we already have it.
TransitionRequest rq{ fb, aspectBit, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
curRenderStep_->preTransitions.insert(rq); // Note that insert avoids inserting duplicates.
return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView;
if (layer == -1 || (layer == 0 && fb->numLayers == 1)) {
return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.imageView : fb->depth.depthSampleView;
} else {
return aspectBit == VK_IMAGE_ASPECT_COLOR_BIT ? fb->color.layerViews[layer] : fb->depth.layerViews[layer];
}
}
// Called on main thread.

View file

@ -24,13 +24,19 @@
// Forward declaration
VK_DEFINE_HANDLE(VmaAllocation);
// 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. Until it's not fine. We'll see.
// Simple independent framebuffer image.
struct VKRImage {
// These four are "immutable".
VkImage image;
// These are 2D if single layer, 2D_ARRAY if multiple. Need to take that into account when picking
// shaders, or use layerViews below and multiple passes to work around it.
VkImageView imageView;
VkImageView depthSampleView;
// If it's a layered image (for stereo), this is two 2D views of it, to make it compatible with shaders that don't yet support stereo.
VkImageView layerViews[2]{};
VmaAllocation alloc;
VkFormat format;
@ -40,6 +46,9 @@ struct VKRImage {
// For debugging.
std::string tag;
};
// NOTE: If numLayers > 1, it will create an array texture, rather than a normal 2D texture.
// This requires a different sampling path!
void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, int numLayers, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag);
class VKRFramebuffer {
@ -47,7 +56,7 @@ public:
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, bool createDepthStencilBuffer, const char *tag);
~VKRFramebuffer();
VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType);
VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType, int layer);
int width = 0;
int height = 0;
@ -65,7 +74,7 @@ public:
// TODO: Hide.
VulkanContext *vulkan_;
private:
VkFramebuffer framebuf[RP_TYPE_COUNT]{};
VkFramebuffer framebuf[RP_TYPE_COUNT][2]{};
std::string tag_;
};
@ -213,7 +222,7 @@ public:
// Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown.
void Wipe();
// This starts a new step containing a render pass.
// This starts a new step containing a render pass (unless it can be trivially merged into the previous one, which is pretty common).
//
// After a "CopyFramebuffer" or the other functions that start "steps", you need to call this beforce
// making any new render state changes or draw calls.
@ -228,11 +237,16 @@ public:
//
// It can be useful to use GetCurrentStepId() to figure out when you need to send all this state again, if you're
// not keeping track of your calls to this function on your own.
void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag);
//
// For layer, we use the same convention as thin3d, where layer = -1 means all layers together. For texturing, that means that you
// get an array texture view.
void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, int layer, const char *tag);
// Returns an ImageView corresponding to a framebuffer. Is called BindFramebufferAsTexture to maintain a similar interface
// as the other backends, even though there's no actual binding happening here.
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits);
// For layer, we use the same convention as thin3d, where layer = -1 means all layers together. For texturing, that means that you
// get an array texture view.
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, VkImageAspectFlags aspectBits, int layer);
void BindCurrentFramebufferAsInputAttachment0(VkImageAspectFlags aspectBits);
@ -256,6 +270,15 @@ public:
compileMutex_.unlock();
}
// We always pass in desc set 0 directly in draw commands. This is used only to bind higher descriptor sets.
void BindDescriptorSet(int index, const VkDescriptorSet descSet) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
VkRenderData data{ VKRRenderCommand::BIND_DESCRIPTOR_SET };
data.bindDescSet.setIndex = index;
data.bindDescSet.descSet = descSet;
curRenderStep_->commands.push_back(data);
}
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != nullptr);

View file

@ -404,11 +404,8 @@ public:
DataFormat PreferredFramebufferReadbackFormat(Framebuffer *src) override;
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
Framebuffer *GetCurrentRenderTarget() override {
return (Framebuffer *)curFramebuffer_.ptr;
}
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit) override;
void BindFramebufferAsRenderTarget(Framebuffer *fbo, int layer, const RenderPassInfo &rp, const char *tag) override;
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;
void BindCurrentFramebufferForColorInput() override;
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
@ -1505,7 +1502,7 @@ private:
Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) {
VkCommandBuffer cmd = renderManager_.GetInitCmd();
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.z_stencil, desc.numLayers, desc.tag);
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.numLayers, desc.z_stencil, desc.tag);
return new VKFramebuffer(vkrfb);
}
@ -1556,17 +1553,17 @@ DataFormat VKContext::PreferredFramebufferReadbackFormat(Framebuffer *src) {
return DrawContext::PreferredFramebufferReadbackFormat(src);
}
void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) {
void VKContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, int layer, const RenderPassInfo &rp, const char *tag) {
VKFramebuffer *fb = (VKFramebuffer *)fbo;
VKRRenderPassLoadAction color = (VKRRenderPassLoadAction)rp.color;
VKRRenderPassLoadAction depth = (VKRRenderPassLoadAction)rp.depth;
VKRRenderPassLoadAction stencil = (VKRRenderPassLoadAction)rp.stencil;
renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, stencil, rp.clearColor, rp.clearDepth, rp.clearStencil, tag);
renderManager_.BindFramebufferAsRenderTarget(fb ? fb->GetFB() : nullptr, color, depth, stencil, rp.clearColor, rp.clearDepth, rp.clearStencil, layer, tag);
curFramebuffer_ = fb;
}
void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit) {
void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {
VKFramebuffer *fb = (VKFramebuffer *)fbo;
_assert_(binding < MAX_BOUND_TEXTURES);
@ -1588,7 +1585,7 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne
}
boundTextures_[binding] = nullptr;
boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect);
boundImageView_[binding] = renderManager_.BindFramebufferAsTexture(fb->GetFB(), binding, aspect, layer);
}
void VKContext::BindCurrentFramebufferForColorInput() {

View file

@ -415,9 +415,11 @@ class Framebuffer : public RefCountedObject {
public:
int Width() { return width_; }
int Height() { return height_; }
int Layers() { return layers_; }
virtual void UpdateTag(const char *tag) {}
protected:
int width_ = -1, height_ = -1;
int width_ = -1, height_ = -1, layers_ = 1;
};
class Buffer : public RefCountedObject {
@ -593,6 +595,8 @@ struct RenderPassInfo {
const char *tag;
};
const int ALL_LAYERS = -1;
class DrawContext {
public:
virtual ~DrawContext();
@ -654,11 +658,10 @@ public:
// These functions should be self explanatory.
// Binding a zero render target means binding the backbuffer.
virtual void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) = 0;
virtual Framebuffer *GetCurrentRenderTarget() = 0;
virtual void BindFramebufferAsRenderTarget(Framebuffer *fbo, int layer, const RenderPassInfo &rp, const char *tag) = 0;
// binding must be < MAX_TEXTURE_SLOTS (0, 1 are okay if it's 2).
virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit) = 0;
virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) = 0;
// Framebuffer fetch / input attachment support, needs to be explicit in Vulkan.
virtual void BindCurrentFramebufferForColorInput() {}

View file

@ -85,7 +85,7 @@ void UIScreen::preRender() {
}
draw->BeginFrame();
// Bind and clear the back buffer
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "UI");
draw->BindFramebufferAsRenderTarget(nullptr, ALL_LAYERS, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "UI");
screenManager()->getUIContext()->BeginFrame();
Draw::Viewport viewport;

View file

@ -89,6 +89,9 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
bool doTextureProjection = id.Bit(FS_BIT_DO_TEXTURE_PROJ);
bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA);
bool arrayTexture = id.Bit(FS_BIT_SAMPLE_ARRAY_TEXTURE);
bool arrayTextureFramebuffer = id.Bit(FS_BIT_FRAMEBUFFER_ARRAY_TEXTURE);
bool flatBug = bugs.Has(Draw::Bugs::BROKEN_FLAT_IN_SHADER) && g_Config.bVendorBugChecksEnabled;
bool doFlatShading = id.Bit(FS_BIT_FLATSHADE) && !flatBug;
@ -155,11 +158,11 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
WRITE(p, "layout (std140, set = 0, binding = 3) uniform baseUBO {\n%s};\n", ub_baseStr);
if (doTexture) {
WRITE(p, "layout (binding = 0) uniform %s tex;\n", texture3D ? "sampler3D" : "sampler2D");
WRITE(p, "layout (binding = 0) uniform %s%s tex;\n", texture3D ? "sampler3D" : "sampler2D", arrayTexture ? "array" : "");
}
if (readFramebufferTex) {
WRITE(p, "layout (binding = 1) uniform sampler2D fbotex;\n");
WRITE(p, "layout (binding = 1) uniform sampler2D%s fbotex;\n", arrayTextureFramebuffer ? "array" : "");
} else if (fetchFramebuffer) {
WRITE(p, "layout (input_attachment_index = 0, binding = 9) uniform subpassInput inputColor;\n");
if (fragmentShaderFlags) {

View file

@ -863,7 +863,7 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra
if (currentRenderVfb_ && dst != currentRenderVfb_ && tookActions) {
// Will probably just change the name of the current renderpass, since one was started by the reinterpret itself.
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After Reinterpret");
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, 0, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "After Reinterpret");
}
shaderManager_->DirtyLastShader();
@ -1011,7 +1011,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe
if (useBufferedRendering_) {
if (vfb->fbo) {
shaderManager_->DirtyLastShader();
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "FBSwitch");
draw_->BindFramebufferAsRenderTarget(vfb->fbo, Draw::ALL_LAYERS, {Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP}, "FBSwitch");
} else {
// This should only happen very briefly when toggling useBufferedRendering_.
ResizeFramebufFBO(vfb, vfb->width, vfb->height, true);
@ -1115,7 +1115,7 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
} else {
flags = DRAWTEX_LINEAR;
}
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag);
draw_->BindFramebufferAsRenderTarget(vfb->fbo, Draw::ALL_LAYERS, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag);
SetViewport2D(0, 0, vfb->renderWidth, vfb->renderHeight);
draw_->SetScissorRect(0, 0, vfb->renderWidth, vfb->renderHeight);
} else {
@ -1153,7 +1153,7 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
}
}
bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags) {
bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags, int layer) {
if (!framebuffer->fbo || !useBufferedRendering_) {
draw_->BindTexture(stage, nullptr);
gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE;
@ -1173,17 +1173,17 @@ bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualF
if (renderCopy) {
VirtualFramebuffer copyInfo = *framebuffer;
copyInfo.fbo = renderCopy;
CopyFramebufferForColorTexture(&copyInfo, framebuffer, flags);
CopyFramebufferForColorTexture(&copyInfo, framebuffer, flags, layer);
RebindFramebuffer("After BindFramebufferAsColorTexture");
draw_->BindFramebufferAsTexture(renderCopy, stage, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(renderCopy, stage, Draw::FB_COLOR_BIT, layer);
gpuStats.numCopiesForSelfTex++;
} else {
// Failed to get temp FBO? Weird.
draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, layer);
}
return true;
} else if (framebuffer != currentRenderVfb_ || (flags & BINDFBCOLOR_FORCE_SELF) != 0) {
draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::FB_COLOR_BIT, layer);
return true;
} else {
ERROR_LOG_REPORT_ONCE(selfTextureFail, G3D, "Attempting to texture from target (src=%08x / target=%08x / flags=%d)", framebuffer->fb_address, currentRenderVfb_->fb_address, flags);
@ -1197,7 +1197,7 @@ bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualF
}
}
void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags) {
void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags, int layer) {
int x = 0;
int y = 0;
int w = src->drawnWidth;
@ -1369,7 +1369,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
DEBUG_LOG(FRAMEBUF, "Display disabled, displaying only black");
// No framebuffer to display! Clear to black.
if (useBufferedRendering_) {
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "CopyDisplayToOutput");
draw_->BindFramebufferAsRenderTarget(nullptr, 0, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "CopyDisplayToOutput");
}
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE);
return;
@ -1433,7 +1433,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) {
// No framebuffer to display! Clear to black.
if (useBufferedRendering_) {
// Bind and clear the backbuffer. This should be the first time during the frame that it's bound.
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "CopyDisplayToOutput_NoFBO");
draw_->BindFramebufferAsRenderTarget(nullptr, 0, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "CopyDisplayToOutput_NoFBO");
}
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE);
return;
@ -1619,7 +1619,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
shaderManager_->DirtyLastShader();
char tag[128];
size_t len = FormatFramebufferName(vfb, tag, sizeof(tag));
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, tag });
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), true, tag });
if (Memory::IsVRAMAddress(vfb->fb_address) && vfb->fb_stride != 0) {
NotifyMemInfo(MemBlockFlags::ALLOC, vfb->fb_address, ColorBufferByteSize(vfb), tag, len);
}
@ -1631,7 +1631,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
if (old.fbo) {
INFO_LOG(FRAMEBUF, "Resizing FBO for %08x : %dx%dx%s", vfb->fb_address, w, h, GeBufferFormatToString(vfb->fb_format));
if (vfb->fbo) {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO");
draw_->BindFramebufferAsRenderTarget(vfb->fbo, 0, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO");
if (!skipCopy) {
BlitFramebuffer(vfb, 0, 0, &old, 0, 0, std::min((u16)oldWidth, std::min(vfb->bufferWidth, vfb->width)), std::min((u16)oldHeight, std::min(vfb->height, vfb->bufferHeight)), 0, RASTER_COLOR, "BlitColor_ResizeFramebufFBO");
}
@ -1640,9 +1640,9 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
}
}
fbosToDelete_.push_back(old.fbo);
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "ResizeFramebufFBO");
draw_->BindFramebufferAsRenderTarget(vfb->fbo, Draw::ALL_LAYERS, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "ResizeFramebufFBO");
} else {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO");
draw_->BindFramebufferAsRenderTarget(vfb->fbo, Draw::ALL_LAYERS, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "ResizeFramebufFBO");
}
currentRenderVfb_ = vfb;
@ -1986,7 +1986,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd
char name[64];
snprintf(name, sizeof(name), "%08x_color_RAM", vfb->fb_address);
textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_CREATED);
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, name });
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), true, name });
vfbs_.push_back(vfb);
u32 byteSize = ColorBufferByteSize(vfb);
@ -2407,7 +2407,8 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u
bool z_stencil = reason == TempFBO::STENCIL;
char name[128];
snprintf(name, sizeof(name), "temp_fbo_%dx%d%s", w / renderScaleFactor_, h / renderScaleFactor_, z_stencil ? "_depth" : "");
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, z_stencil, name });
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, GetFramebufferLayers(), z_stencil, name });
if (!fbo) {
return nullptr;
}
@ -2784,10 +2785,10 @@ void FramebufferManagerCommon::RebindFramebuffer(const char *tag) {
draw_->InvalidateCachedState();
shaderManager_->DirtyLastShader();
if (currentRenderVfb_ && currentRenderVfb_->fbo) {
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag);
draw_->BindFramebufferAsRenderTarget(currentRenderVfb_->fbo, Draw::ALL_LAYERS, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag);
} else {
// Should this even happen? It could while debugging, but maybe we can just skip binding at all.
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "RebindFramebuffer_Bad");
draw_->BindFramebufferAsRenderTarget(nullptr, 0, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "RebindFramebuffer_Bad");
}
}
@ -2906,7 +2907,7 @@ void FramebufferManagerCommon::BlitFramebuffer(VirtualFramebuffer *dst, int dstX
// This can happen if they recently switched from non-buffered.
if (useBufferedRendering_) {
// Just bind the back buffer for rendering, forget about doing anything else as we're in a weird state.
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "BlitFramebuffer");
draw_->BindFramebufferAsRenderTarget(nullptr, 0, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, "BlitFramebuffer");
}
return;
}
@ -3029,8 +3030,8 @@ void FramebufferManagerCommon::BlitUsingRaster(
// Unbind the texture first to avoid the D3D11 hazard check (can't set render target to things bound as textures and vice versa, not even temporarily).
draw_->BindTexture(0, nullptr);
// This will get optimized away in case it's already bound (in VK and GL at least..)
draw_->BindFramebufferAsRenderTarget(dest, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag ? tag : "BlitUsingRaster");
draw_->BindFramebufferAsTexture(src, 0, pipeline->info.readChannel == RASTER_COLOR ? Draw::FB_COLOR_BIT : Draw::FB_DEPTH_BIT);
draw_->BindFramebufferAsRenderTarget(dest, 0, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::KEEP }, tag ? tag : "BlitUsingRaster");
draw_->BindFramebufferAsTexture(src, 0, pipeline->info.readChannel == RASTER_COLOR ? Draw::FB_COLOR_BIT : Draw::FB_DEPTH_BIT, 0);
if (destX1 == 0.0f && destY1 == 0.0f && destX2 >= destW && destY2 >= destH) {
// We overwrite the whole channel of the framebuffer, so we can invalidate the current contents.
@ -3046,6 +3047,14 @@ void FramebufferManagerCommon::BlitUsingRaster(
gstate_c.Dirty(DIRTY_ALL_RENDER_STATE);
}
int FramebufferManagerCommon::GetFramebufferLayers() const {
int layers = 1;
if (gstate_c.Use(GPU_USE_SINGLE_PASS_STEREO)) {
layers = 2;
}
return layers;
}
VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(VirtualFramebuffer *src, GEBufferFormat newFormat) {
// Look for an identical framebuffer with the new format
_dbg_assert_(src->fb_format != newFormat);
@ -3089,7 +3098,7 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi
char tag[128];
FormatFramebufferName(vfb, tag, sizeof(tag));
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, 1, true, tag });
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), true, tag });
vfbs_.push_back(vfb);
}

View file

@ -322,7 +322,7 @@ public:
// Otherwise it doesn't get called.
void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);
bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags);
bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags, int layer);
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel);
void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);
@ -460,7 +460,7 @@ protected:
// Used by ReadFramebufferToMemory and later framebuffer block copies
void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag);
void CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags);
void CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags, int layer);
void EstimateDrawingSize(u32 fb_address, int fb_stride, GEBufferFormat fb_format, int viewport_width, int viewport_height, int region_width, int region_height, int scissor_width, int scissor_height, int &drawing_width, int &drawing_height);
u32 ColorBufferByteSize(const VirtualFramebuffer *vfb) const;
@ -486,6 +486,8 @@ protected:
void UpdateFramebufUsage(VirtualFramebuffer *vfb);
int GetFramebufferLayers() const;
static void SetColorUpdated(VirtualFramebuffer *dstBuffer, int skipDrawReason) {
dstBuffer->memoryUpdated = false;
dstBuffer->clutUpdatedBytes = 0;

View file

@ -566,7 +566,7 @@ void PresentationCommon::BindSource(int binding) {
if (srcTexture_) {
draw_->BindTexture(binding, srcTexture_);
} else if (srcFramebuffer_) {
draw_->BindFramebufferAsTexture(srcFramebuffer_, binding, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(srcFramebuffer_, binding, Draw::FB_COLOR_BIT, 0);
} else {
_assert_(false);
}
@ -685,13 +685,13 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
PostShaderUniforms uniforms;
const auto performShaderPass = [&](const ShaderInfo *shaderInfo, Draw::Framebuffer *postShaderFramebuffer, Draw::Pipeline *postShaderPipeline) {
if (postShaderOutput) {
draw_->BindFramebufferAsTexture(postShaderOutput, 0, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(postShaderOutput, 0, Draw::FB_COLOR_BIT, 0);
} else {
BindSource(0);
}
BindSource(1);
if (shaderInfo->usePreviousFrame)
draw_->BindFramebufferAsTexture(previousFramebuffer, 2, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(previousFramebuffer, 2, Draw::FB_COLOR_BIT, 0);
int nextWidth, nextHeight;
draw_->GetFramebufferDimensions(postShaderFramebuffer, &nextWidth, &nextHeight);
@ -743,7 +743,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
postShaderFramebuffer = previousFramebuffers_[previousIndex_];
}
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "PostShader");
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, 0, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "PostShader");
performShaderPass(shaderInfo, postShaderFramebuffer, postShaderPipeline);
}
@ -764,7 +764,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
previousIndex_ = 0;
Draw::Framebuffer *postShaderFramebuffer = previousFramebuffers_[previousIndex_];
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "InterFrameBlit");
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, 0, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "InterFrameBlit");
performShaderPass(shaderInfo, postShaderFramebuffer, postShaderPipeline);
}
@ -773,13 +773,13 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u
pipeline = postShaderPipelines_.back();
}
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "FinalBlit");
draw_->BindFramebufferAsRenderTarget(nullptr, 0, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "FinalBlit");
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
draw_->BindPipeline(pipeline);
if (postShaderOutput) {
draw_->BindFramebufferAsTexture(postShaderOutput, 0, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(postShaderOutput, 0, Draw::FB_COLOR_BIT, 0);
} else {
BindSource(0);
}

View file

@ -362,6 +362,15 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip
id.SetBit(FS_BIT_COLOR_WRITEMASK, colorWriteMask);
// Stereo support
if (gstate_c.Use(GPU_USE_SINGLE_PASS_STEREO)) {
// All framebuffers are array textures in this mode.
if (gstate_c.arrayTexture) {
id.SetBit(FS_BIT_SAMPLE_ARRAY_TEXTURE);
}
id.SetBit(FS_BIT_FRAMEBUFFER_ARRAY_TEXTURE);
}
if (g_Config.bVendorBugChecksEnabled && bugs.Has(Draw::Bugs::NO_DEPTH_CANNOT_DISCARD_STENCIL)) {
bool stencilWithoutDepth = !IsStencilTestOutputDisabled() && (!gstate.isDepthTestEnabled() || !gstate.isDepthWriteEnabled());
if (stencilWithoutDepth) {

View file

@ -98,6 +98,8 @@ enum FShaderBit : uint8_t {
FS_BIT_COLOR_WRITEMASK = 50,
FS_BIT_REPLACE_LOGIC_OP = 51, // 4 bits. GE_LOGIC_COPY means no-op/off.
FS_BIT_SHADER_DEPAL_MODE = 55, // 2 bits (ShaderDepalMode)
FS_BIT_SAMPLE_ARRAY_TEXTURE = 57, // For multiview, framebuffers are array textures and we need to sample the two layers correctly.
FS_BIT_FRAMEBUFFER_ARRAY_TEXTURE = 58, // Framebuffer texture might also be stereo.
};
static inline FShaderBit operator +(FShaderBit bit, int i) {

View file

@ -131,6 +131,16 @@ static const char * const ub_vs_bonesStr =
R"( mat3x4 u_bone0; mat3x4 u_bone1; mat3x4 u_bone2; mat3x4 u_bone3; mat3x4 u_bone4; mat3x4 u_bone5; mat3x4 u_bone6; mat3x4 u_bone7; mat3x4 u_bone8;
)";
static const char * const ub_frame_globalstr =
R"( vec4 unused;
)";
// VR stuff will go here.
struct UB_FrameGlobal {
float unused[4];
};
void CalcCullRange(float minValues[4], float maxValues[4], bool flipViewport, bool hasNegZ);
void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipViewport, bool useBufferedRendering);

View file

@ -187,7 +187,7 @@ bool FramebufferManagerCommon::PerformWriteStencilFromMemory(u32 addr, int size,
// Otherwise, we can skip alpha in many cases, in which case we don't even use a shader.
if (flags & WriteStencil::IGNORE_ALPHA) {
if (dstBuffer->fbo) {
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }, "WriteStencilFromMemory_Clear");
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, 0, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }, "WriteStencilFromMemory_Clear");
}
return true;
}
@ -281,9 +281,9 @@ bool FramebufferManagerCommon::PerformWriteStencilFromMemory(u32 addr, int size,
Draw::Framebuffer *blitFBO = nullptr;
if (useBlit) {
blitFBO = GetTempFBO(TempFBO::STENCIL, w, h);
draw_->BindFramebufferAsRenderTarget(blitFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::CLEAR }, "WriteStencilFromMemory_Blit");
draw_->BindFramebufferAsRenderTarget(blitFBO, 0, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::CLEAR }, "WriteStencilFromMemory_Blit");
} else if (dstBuffer->fbo) {
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }, "WriteStencilFromMemory_NoBlit");
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, 0, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR }, "WriteStencilFromMemory_NoBlit");
}
Draw::Viewport viewport = { 0.0f, 0.0f, (float)w, (float)h, 0.0f, 1.0f };

View file

@ -2121,7 +2121,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
// Very icky conflation here of native and thin3d rendering. This will need careful work per backend in BindAsClutTexture.
BindAsClutTexture(clutTexture.texture, smoothedDepal);
framebufferManager_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET);
framebufferManager_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET, 0);
// Vulkan needs to do some extra work here to pick out the native handle from Draw.
BoundFramebufferTexture();
@ -2191,13 +2191,13 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
Draw::Framebuffer *depalFBO = framebufferManager_->GetTempFBO(TempFBO::DEPAL, depalWidth, framebuffer->renderHeight);
draw_->BindTexture(0, nullptr);
draw_->BindTexture(1, nullptr);
draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Depal");
draw_->BindFramebufferAsRenderTarget(depalFBO, Draw::ALL_LAYERS, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Depal");
draw_->InvalidateFramebuffer(Draw::FB_INVALIDATION_STORE, Draw::FB_DEPTH_BIT | Draw::FB_STENCIL_BIT);
draw_->SetScissorRect(u1, v1, u2 - u1, v2 - v1);
Draw::Viewport vp{ 0.0f, 0.0f, (float)depalWidth, (float)framebuffer->renderHeight, 0.0f, 1.0f };
draw_->SetViewports(1, &vp);
draw_->BindFramebufferAsTexture(framebuffer->fbo, 0, depth ? Draw::FB_DEPTH_BIT : Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(framebuffer->fbo, 0, depth ? Draw::FB_DEPTH_BIT : Draw::FB_COLOR_BIT, 0);
draw_->BindTexture(1, clutTexture.texture);
Draw::SamplerState *nearest = textureShaderCache_->GetSampler(false);
Draw::SamplerState *clutSampler = textureShaderCache_->GetSampler(smoothedDepal);
@ -2213,7 +2213,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
draw_->BindTexture(0, nullptr);
framebufferManager_->RebindFramebuffer("ApplyTextureFramebuffer");
draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0);
BoundFramebufferTexture();
const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16);
@ -2226,7 +2226,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
shaderManager_->DirtyLastShader();
} else {
framebufferManager_->RebindFramebuffer("ApplyTextureFramebuffer");
framebufferManager_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET);
framebufferManager_->BindFramebufferAsColorTexture(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET, 0);
BoundFramebufferTexture();
gstate_c.SetUseShaderDepal(ShaderDepalMode::OFF);
@ -2292,14 +2292,14 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) {
Draw::Framebuffer *depalFBO = framebufferManager_->GetTempFBO(TempFBO::DEPAL, texWidth, texHeight);
draw_->BindTexture(0, nullptr);
draw_->BindTexture(1, nullptr);
draw_->BindFramebufferAsRenderTarget(depalFBO, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Depal");
draw_->BindFramebufferAsRenderTarget(depalFBO, 0, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Depal");
draw_->InvalidateFramebuffer(Draw::FB_INVALIDATION_STORE, Draw::FB_DEPTH_BIT | Draw::FB_STENCIL_BIT);
draw_->SetScissorRect(u1, v1, u2 - u1, v2 - v1);
Draw::Viewport vp{ 0.0f, 0.0f, (float)texWidth, (float)texHeight, 0.0f, 1.0f };
draw_->SetViewports(1, &vp);
draw_->BindNativeTexture(0, GetNativeTextureView(entry));
draw_->BindFramebufferAsTexture(dynamicClutFbo_, 1, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(dynamicClutFbo_, 1, Draw::FB_COLOR_BIT, 0);
Draw::SamplerState *nearest = textureShaderCache_->GetSampler(false);
Draw::SamplerState *clutSampler = textureShaderCache_->GetSampler(false);
draw_->BindSamplerStates(0, 1, &nearest);
@ -2314,7 +2314,7 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) {
draw_->BindTexture(0, nullptr);
framebufferManager_->RebindFramebuffer("ApplyTextureFramebuffer");
draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT);
draw_->BindFramebufferAsTexture(depalFBO, 0, Draw::FB_COLOR_BIT, 0);
BoundFramebufferTexture();
const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16);

View file

@ -159,7 +159,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState);
if (fboTexBindState == FBO_TEX_COPY_BIND_TEX) {
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY, 0);
// No sampler required, we do a plain Load in the pixel shader.
fboTexBound_ = true;
fboTexBindState = FBO_TEX_NONE;

View file

@ -101,7 +101,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
if (fboTexBindState_ = FBO_TEX_COPY_BIND_TEX) {
// Note that this is positions, not UVs, that we need the copy from.
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY, 0);
// If we are rendering at a higher resolution, linear is probably best for the dest color.
device_->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
device_->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
@ -139,7 +139,7 @@ void DrawEngineDX9::ApplyDrawState(int prim) {
if (fboTexBindState_ == FBO_TEX_COPY_BIND_TEX) {
// Note that this is positions, not UVs, that we need the copy from.
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY, Draw::ALL_LAYERS);
// If we are rendering at a higher resolution, linear is probably best for the dest color.
device_->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
device_->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

View file

@ -204,11 +204,11 @@ bool FramebufferManagerGLES::ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int
shaderManager_->DirtyLastShader();
auto *blitFBO = GetTempFBO(TempFBO::COPY, fbo->Width(), fbo->Height());
draw_->BindFramebufferAsRenderTarget(blitFBO, { RPAction::DONT_CARE, RPAction::DONT_CARE, RPAction::DONT_CARE }, "ReadbackDepthbufferSync");
draw_->BindFramebufferAsRenderTarget(blitFBO, 0, { RPAction::DONT_CARE, RPAction::DONT_CARE, RPAction::DONT_CARE }, "ReadbackDepthbufferSync");
Draw::Viewport viewport = { 0.0f, 0.0f, (float)fbo->Width(), (float)fbo->Height(), 0.0f, 1.0f };
draw_->SetViewports(1, &viewport);
draw_->BindFramebufferAsTexture(fbo, TEX_SLOT_PSP_TEXTURE, FB_DEPTH_BIT);
draw_->BindFramebufferAsTexture(fbo, TEX_SLOT_PSP_TEXTURE, FB_DEPTH_BIT, 0);
draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, &depthReadbackSampler_);
// We must bind the program after starting the render pass.
@ -322,11 +322,11 @@ bool FramebufferManagerGLES::ReadbackStencilbufferSync(Draw::Framebuffer *fbo, i
shaderManager_->DirtyLastShader();
auto *blitFBO = GetTempFBO(TempFBO::COPY, fbo->Width(), fbo->Height());
draw_->BindFramebufferAsRenderTarget(blitFBO, { RPAction::DONT_CARE, RPAction::DONT_CARE, RPAction::DONT_CARE }, "ReadbackStencilbufferSync");
draw_->BindFramebufferAsRenderTarget(blitFBO, 0, { RPAction::DONT_CARE, RPAction::DONT_CARE, RPAction::DONT_CARE }, "ReadbackStencilbufferSync");
Draw::Viewport viewport = { 0.0f, 0.0f, (float)fbo->Width(), (float)fbo->Height(), 0.0f, 1.0f };
draw_->SetViewports(1, &viewport);
draw_->BindFramebufferAsTexture(fbo, TEX_SLOT_PSP_TEXTURE, FB_STENCIL_BIT);
draw_->BindFramebufferAsTexture(fbo, TEX_SLOT_PSP_TEXTURE, FB_STENCIL_BIT, 0);
draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, &stencilReadbackSampler_);
// We must bind the program after starting the render pass.

View file

@ -54,9 +54,9 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb)
// Discard the previous contents of this buffer where possible.
if (gl_extensions.GLES3) {
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "UpdateDownloadTempBuffer");
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, 0, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "UpdateDownloadTempBuffer");
} else if (gl_extensions.IsGLES) {
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "UpdateDownloadTempBuffer");
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, 0, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "UpdateDownloadTempBuffer");
gstate_c.Dirty(DIRTY_BLEND_STATE);
}
}

View file

@ -158,7 +158,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
// fboTexNeedsBind_ won't be set if we can read directly from the target.
if (fboTexBindState == FBO_TEX_COPY_BIND_TEX) {
// Note that this is positions, not UVs, that we need the copy from.
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY, 0);
// If we are rendering at a higher resolution, linear is probably best for the dest color.
renderManager->SetTextureSampler(1, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f);
fboTexBound_ = true;

View file

@ -583,6 +583,7 @@ struct GPUStateCache {
bool bgraTexture;
bool needShaderTexClamp;
bool arrayTexture;
float morphWeights[8];
u32 deferredVertTypeDirty;

View file

@ -618,7 +618,7 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) {
u1 = 1.0f;
}
if (!hasImage) {
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "CopyToCurrentFboFromDisplayRam");
draw_->BindFramebufferAsRenderTarget(nullptr, 0, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "CopyToCurrentFboFromDisplayRam");
return;
}

View file

@ -42,6 +42,7 @@
#include "GPU/Common/VertexDecoderCommon.h"
#include "GPU/Common/SoftwareTransformCommon.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "GPU/Common/ShaderUniforms.h"
#include "GPU/Debugger/Debugger.h"
#include "GPU/Vulkan/DrawEngineVulkan.h"
#include "GPU/Vulkan/TextureCacheVulkan.h"
@ -184,8 +185,9 @@ void DrawEngineVulkan::InitDeviceObjects() {
VkPipelineLayoutCreateInfo pl{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pl.pPushConstantRanges = nullptr;
pl.pushConstantRangeCount = 0;
pl.setLayoutCount = 1;
pl.pSetLayouts = &descriptorSetLayout_;
VkDescriptorSetLayout layouts[1] = { descriptorSetLayout_ };
pl.setLayoutCount = ARRAY_SIZE(layouts);
pl.pSetLayouts = layouts;
pl.flags = 0;
res = vkCreatePipelineLayout(device, &pl, nullptr, &pipelineLayout_);
_dbg_assert_(VK_SUCCESS == res);
@ -303,6 +305,7 @@ void DrawEngineVulkan::BeginFrame() {
frame->pushIndex->Reset();
VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);
frame->pushUBO->Begin(vulkan);
frame->pushVertex->Begin(vulkan);
frame->pushIndex->Begin(vulkan);

View file

@ -211,8 +211,9 @@ private:
Draw::DrawContext *draw_;
// We use a single descriptor set layout for all PSP draws.
// We use a shared descriptor set layout for all PSP draws.
VkDescriptorSetLayout descriptorSetLayout_;
VkPipelineLayout pipelineLayout_;
VulkanPipeline *lastPipeline_;
VkDescriptorSet lastDs_ = VK_NULL_HANDLE;

View file

@ -268,6 +268,11 @@ u32 GPU_Vulkan::CheckGPUFeatures() const {
features |= GPU_ROUND_DEPTH_TO_16BIT;
}
if (true) {
features |= GPU_USE_SINGLE_PASS_STEREO;
features &= ~GPU_USE_FRAMEBUFFER_FETCH; // Need to figure out if this can be supported with multiview rendering
}
return features;
}

View file

@ -365,7 +365,7 @@ void DrawEngineVulkan::BindShaderBlendTex() {
// Set the nearest/linear here (since we correctly know if alpha/color tests are needed)?
if (!gstate.isModeClear()) {
if (fboTexBindState_ == FBO_TEX_COPY_BIND_TEX) {
bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY);
bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY, Draw::ALL_LAYERS);
_dbg_assert_(bindResult);
boundSecondary_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE1_IMAGEVIEW);
boundSecondaryIsInputAttachment_ = false;

View file

@ -1359,9 +1359,9 @@ void EmuScreen::preRender() {
if ((!useBufferedRendering && !g_Config.bSoftwareRendering) || Core_IsStepping()) {
// We need to clear here already so that drawing during the frame is done on a clean slate.
if (Core_IsStepping() && gpuStats.numFlips != 0) {
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_BackBuffer");
draw->BindFramebufferAsRenderTarget(nullptr, 0, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_BackBuffer");
} else {
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "EmuScreen_BackBuffer");
draw->BindFramebufferAsRenderTarget(nullptr, 0, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "EmuScreen_BackBuffer");
}
Viewport viewport;
@ -1400,7 +1400,7 @@ void EmuScreen::render() {
// It's possible this might be set outside PSP_RunLoopFor().
// In this case, we need to double check it here.
checkPowerDown();
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_Invalid");
thin3d->BindFramebufferAsRenderTarget(nullptr, 0, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_Invalid");
renderUI();
return;
}
@ -1439,12 +1439,12 @@ void EmuScreen::render() {
// Clear to blue background screen
bool dangerousSettings = !Reporting::IsSupported();
uint32_t color = dangerousSettings ? 0xFF900050 : 0xFF900000;
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError");
thin3d->BindFramebufferAsRenderTarget(nullptr, 0, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError");
// The info is drawn later in renderUI
} else {
// If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output.
// This won't work in non-buffered, but that's fine.
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
thin3d->BindFramebufferAsRenderTarget(nullptr, 0, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
// Just to make sure.
if (PSP_IsInited()) {
gpu->CopyDisplayToOutput(true);
@ -1456,7 +1456,7 @@ void EmuScreen::render() {
// Didn't actually reach the end of the frame, ran out of the blockTicks cycles.
// In this case we need to bind and wipe the backbuffer, at least.
// It's possible we never ended up outputted anything - make sure we have the backbuffer cleared
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame");
thin3d->BindFramebufferAsRenderTarget(nullptr, 0, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame");
break;
}
@ -1470,7 +1470,7 @@ void EmuScreen::render() {
if (hasVisibleUI()) {
// In most cases, this should already be bound and a no-op.
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_UI");
thin3d->BindFramebufferAsRenderTarget(nullptr, 0, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_UI");
cardboardDisableButton_->SetVisibility(g_Config.bEnableCardboardVR ? UI::V_VISIBLE : UI::V_GONE);
screenManager()->getUIContext()->BeginFrame();
renderUI();

View file

@ -1498,6 +1498,7 @@
<None Include="..\android\jni\Locals.mk" />
<None Include="..\appveyor.yml" />
<None Include="..\assets\compat.ini" />
<None Include="..\assets\compatvr.ini" />
<None Include="..\assets\knownfuncs.ini" />
<None Include="..\assets\langregion.ini" />
<None Include="..\atlasscript.txt" />

View file

@ -711,6 +711,9 @@
<None Include="..\SDL\buildassets.sh">
<Filter>Other Platforms\SDL</Filter>
</None>
<None Include="..\assets\compatvr.ini">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ppsspp.rc">

View file

@ -240,7 +240,7 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const
PSP_EndHostFrame();
if (draw) {
draw->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Headless");
draw->BindFramebufferAsRenderTarget(nullptr, 0, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Headless");
// Vulkan may get angry if we don't do a final present.
if (gpu)
gpu->CopyDisplayToOutput(true);