mirror of
https://github.com/libretro/ppsspp.git
synced 2025-02-04 16:06:13 +00:00
Merge pull request #8680 from hrydgard/vulkan-framebuffer
Vulkan framebuffer - First steps
This commit is contained in:
commit
00d60d0068
@ -31,7 +31,7 @@ using namespace std;
|
||||
|
||||
static const char *validationLayers[] = {
|
||||
"VK_LAYER_GOOGLE_unique_objects",
|
||||
//"VK_LAYER_LUNARG_standard_validation",
|
||||
"VK_LAYER_LUNARG_standard_validation",
|
||||
/*
|
||||
"VK_LAYER_GOOGLE_threading",
|
||||
"VK_LAYER_LUNARG_draw_state",
|
||||
|
@ -74,6 +74,7 @@ public:
|
||||
void QueueDeleteImageView(VkImageView imageView) { imageViews_.push_back(imageView); }
|
||||
void QueueDeleteDeviceMemory(VkDeviceMemory deviceMemory) { deviceMemory_.push_back(deviceMemory); }
|
||||
void QueueDeleteSampler(VkSampler sampler) { samplers_.push_back(sampler); }
|
||||
void QueueDeletePipeline(VkPipeline pipeline) { pipelines_.push_back(pipeline); }
|
||||
void QueueDeletePipelineCache(VkPipelineCache pipelineCache) { pipelineCaches_.push_back(pipelineCache); }
|
||||
void QueueDeleteRenderPass(VkRenderPass renderPass) { renderPasses_.push_back(renderPass); }
|
||||
void QueueDeleteFramebuffer(VkFramebuffer framebuffer) { framebuffers_.push_back(framebuffer); }
|
||||
@ -88,6 +89,7 @@ public:
|
||||
assert(imageViews_.size() == 0);
|
||||
assert(deviceMemory_.size() == 0);
|
||||
assert(samplers_.size() == 0);
|
||||
assert(pipelines_.size() == 0);
|
||||
assert(pipelineCaches_.size() == 0);
|
||||
assert(renderPasses_.size() == 0);
|
||||
assert(framebuffers_.size() == 0);
|
||||
@ -100,6 +102,7 @@ public:
|
||||
imageViews_ = std::move(del.imageViews_);
|
||||
deviceMemory_ = std::move(del.deviceMemory_);
|
||||
samplers_ = std::move(del.samplers_);
|
||||
pipelines_ = std::move(del.pipelines_);
|
||||
pipelineCaches_ = std::move(del.pipelineCaches_);
|
||||
renderPasses_ = std::move(del.renderPasses_);
|
||||
framebuffers_ = std::move(del.framebuffers_);
|
||||
@ -139,6 +142,10 @@ public:
|
||||
vkDestroySampler(device, sampler, nullptr);
|
||||
}
|
||||
samplers_.clear();
|
||||
for (auto &pipeline : pipelines_) {
|
||||
vkDestroyPipeline(device, pipeline, nullptr);
|
||||
}
|
||||
pipelines_.clear();
|
||||
for (auto &pcache : pipelineCaches_) {
|
||||
vkDestroyPipelineCache(device, pcache, nullptr);
|
||||
}
|
||||
@ -166,6 +173,7 @@ private:
|
||||
std::vector<VkImageView> imageViews_;
|
||||
std::vector<VkDeviceMemory> deviceMemory_;
|
||||
std::vector<VkSampler> samplers_;
|
||||
std::vector<VkPipeline> pipelines_;
|
||||
std::vector<VkPipelineCache> pipelineCaches_;
|
||||
std::vector<VkRenderPass> renderPasses_;
|
||||
std::vector<VkFramebuffer> framebuffers_;
|
||||
|
@ -29,17 +29,28 @@
|
||||
// Uses integer instructions available since OpenGL 3.0. Suitable for ES 3.0 as well.
|
||||
void GenerateDepalShader300(char *buffer, GEBufferFormat pixelFormat, ShaderLanguage language) {
|
||||
char *p = buffer;
|
||||
if (gl_extensions.IsGLES) {
|
||||
WRITE(p, "#version 300 es\n");
|
||||
WRITE(p, "precision mediump float;\n");
|
||||
if (language == GLSL_VULKAN) {
|
||||
WRITE(p, "#version 140\n");
|
||||
WRITE(p, "#extension GL_ARB_separate_shader_objects : enable\n");
|
||||
WRITE(p, "#extension GL_ARB_shading_language_420pack : enable\n");
|
||||
WRITE(p, "layout(set = 0, binding = 0) uniform sampler2D tex;\n");
|
||||
WRITE(p, "layout(set = 0, binding = 1) uniform sampler2D pal;\n");
|
||||
WRITE(p, "layout(location = 0) in vec2 v_texcoord0;\n");
|
||||
WRITE(p, "layout(location = 0) out vec4 fragColor0\n;");
|
||||
} else {
|
||||
WRITE(p, "#version 330\n");
|
||||
if (gl_extensions.IsGLES) {
|
||||
WRITE(p, "#version 300 es\n");
|
||||
WRITE(p, "precision mediump float;\n");
|
||||
} else {
|
||||
WRITE(p, "#version 330\n");
|
||||
}
|
||||
WRITE(p, "in vec2 v_texcoord0;\n");
|
||||
WRITE(p, "out vec4 fragColor0;\n");
|
||||
WRITE(p, "uniform sampler2D tex;\n");
|
||||
WRITE(p, "uniform sampler2D pal;\n");
|
||||
}
|
||||
WRITE(p, "in vec2 v_texcoord0;\n");
|
||||
WRITE(p, "out vec4 fragColor0;\n");
|
||||
WRITE(p, "uniform sampler2D tex;\n");
|
||||
WRITE(p, "uniform sampler2D pal;\n");
|
||||
|
||||
// TODO: Add support for integer textures. Though it hardly matters.
|
||||
WRITE(p, "void main() {\n");
|
||||
WRITE(p, " vec4 color = texture(tex, v_texcoord0);\n");
|
||||
|
||||
|
@ -158,7 +158,8 @@ public:
|
||||
// that come from elsewhere than gstate.
|
||||
FramebufferHeuristicParams inputs;
|
||||
GetFramebufferHeuristicInputs(&inputs, gstate);
|
||||
return DoSetRenderFrameBuffer(inputs, skipDrawReason);
|
||||
VirtualFramebuffer *vfb = DoSetRenderFrameBuffer(inputs, skipDrawReason);
|
||||
return vfb;
|
||||
}
|
||||
}
|
||||
virtual void RebindFramebuffer() = 0;
|
||||
|
@ -208,6 +208,7 @@ DrawEngineVulkan::~DrawEngineVulkan() {
|
||||
delete nullTexture_;
|
||||
}
|
||||
delete[] uvScale;
|
||||
vkDestroyPipelineLayout(vulkan_->GetDevice(), pipelineLayout_, nullptr);
|
||||
}
|
||||
|
||||
void DrawEngineVulkan::BeginFrame() {
|
||||
@ -663,9 +664,11 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
vkCmdSetStencilCompareMask(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilCompareMask);
|
||||
vkCmdSetStencilReference(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilRef);
|
||||
}
|
||||
float bc[4];
|
||||
Uint8x4ToFloat4(bc, dynState.blendColor);
|
||||
vkCmdSetBlendConstants(cmd_, bc);
|
||||
if (dynState.useBlendColor) {
|
||||
float bc[4];
|
||||
Uint8x4ToFloat4(bc, dynState.blendColor);
|
||||
vkCmdSetBlendConstants(cmd_, bc);
|
||||
}
|
||||
|
||||
dirtyUniforms_ |= shaderManager_->UpdateUniforms();
|
||||
|
||||
@ -741,7 +744,6 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
|
||||
// Only here, where we know whether to clear or to draw primitives, should we actually set the current framebuffer! Because that gives use the opportunity
|
||||
// to use a "pre-clear" render pass, for high efficiency on tilers.
|
||||
|
||||
if (result.action == SW_DRAW_PRIMITIVES) {
|
||||
VulkanPipelineRasterStateKey pipelineKey;
|
||||
VulkanDynamicState dynState;
|
||||
@ -758,10 +760,12 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
} else if (dynState.useStencil) {
|
||||
vkCmdSetStencilReference(cmd_, VK_STENCIL_FRONT_AND_BACK, dynState.stencilRef);
|
||||
}
|
||||
if (dynState.useBlendColor) {
|
||||
float bc[4];
|
||||
Uint8x4ToFloat4(bc, dynState.blendColor);
|
||||
vkCmdSetBlendConstants(cmd_, bc);
|
||||
}
|
||||
|
||||
float bc[4];
|
||||
Uint8x4ToFloat4(bc, dynState.blendColor);
|
||||
vkCmdSetBlendConstants(cmd_, bc);
|
||||
dirtyUniforms_ |= shaderManager_->UpdateUniforms();
|
||||
|
||||
shaderManager_->GetShaders(prim, lastVTypeID_, &vshader, &fshader, useHWTransform);
|
||||
@ -800,51 +804,9 @@ void DrawEngineVulkan::DoFlush(VkCommandBuffer cmd) {
|
||||
}
|
||||
} else if (result.action == SW_CLEAR) {
|
||||
// Note: we won't get here if the clear is alpha but not color, or color but not alpha.
|
||||
// A rectangle will be used instead.
|
||||
// TODO: If this is the first clear in a frame, translate to a cleared attachment load instead.
|
||||
|
||||
int mask = gstate.isClearModeColorMask() ? 1 : 0;
|
||||
if (gstate.isClearModeAlphaMask()) mask |= 2;
|
||||
if (gstate.isClearModeDepthMask()) mask |= 4;
|
||||
|
||||
VkClearValue colorValue, depthValue;
|
||||
colorValue.color.float32[0] = (result.color & 0xFF) * (1.0f / 255.0f);
|
||||
colorValue.color.float32[1] = ((result.color >> 8) & 0xFF) * (1.0f / 255.0f);
|
||||
colorValue.color.float32[2] = ((result.color >> 16) & 0xFF) * (1.0f / 255.0f);
|
||||
colorValue.color.float32[3] = ((result.color >> 24) & 0xFF) * (1.0f / 255.0f);
|
||||
depthValue.depthStencil.depth = result.depth;
|
||||
depthValue.depthStencil.stencil = (result.color >> 24) & 0xFF;
|
||||
|
||||
VkClearRect rect;
|
||||
rect.baseArrayLayer = 0;
|
||||
rect.layerCount = 1;
|
||||
rect.rect.offset.x = 0;
|
||||
rect.rect.offset.y = 0;
|
||||
rect.rect.extent.width = gstate_c.curRTRenderWidth;
|
||||
rect.rect.extent.height = gstate_c.curRTRenderHeight;
|
||||
|
||||
int count = 0;
|
||||
VkClearAttachment attach[2];
|
||||
if (mask & 3) {
|
||||
attach[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
attach[count].clearValue = colorValue;
|
||||
attach[count].colorAttachment = 0;
|
||||
count++;
|
||||
}
|
||||
if (mask & 4) {
|
||||
attach[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
attach[count].clearValue = depthValue;
|
||||
attach[count].colorAttachment = 0;
|
||||
count++;
|
||||
}
|
||||
vkCmdClearAttachments(cmd_, count, attach, 1, &rect);
|
||||
|
||||
if (mask & 1) {
|
||||
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
|
||||
}
|
||||
if (mask & 4) {
|
||||
framebufferManager_->SetDepthUpdated();
|
||||
}
|
||||
// We let the framebuffer manager handle the clear. It can use renderpasses to optimize on tilers.
|
||||
framebufferManager_->NotifyClear(gstate.isClearModeColorMask(), gstate.isClearModeAlphaMask(), gstate.isClearModeDepthMask(), result.color, result.depth);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,6 +143,10 @@ public:
|
||||
|
||||
void DirtyAllUBOs();
|
||||
|
||||
VulkanPushBuffer *GetPushBufferForTextureData() {
|
||||
return frame_[curFrame_].pushUBO;
|
||||
}
|
||||
|
||||
private:
|
||||
struct FrameData;
|
||||
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "math/lin/matrix4x4.h"
|
||||
|
||||
#include "Common/Vulkan/VulkanContext.h"
|
||||
#include "Common/Vulkan/VulkanMemory.h"
|
||||
#include "Common/Vulkan/VulkanImage.h"
|
||||
#include "Common/ColorConv.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/MemMap.h"
|
||||
@ -52,45 +54,225 @@
|
||||
|
||||
extern int g_iNumVideos;
|
||||
|
||||
const VkFormat framebufFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
static const char tex_fs[] =
|
||||
"layout (binding = 0) uniform sampler2D sampler0;\n"
|
||||
"layout (location = 0) in vec2 v_texcoord0;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = texture2D(sampler0, v_texcoord0);\n"
|
||||
"}\n";
|
||||
static const char tex_fs[] = R"(#version 400
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
layout (binding = 0) uniform sampler2D sampler0;
|
||||
layout (location = 0) in vec2 v_texcoord0;
|
||||
layout (location = 0) out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = texture(sampler0, v_texcoord0);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char basic_vs[] =
|
||||
"layout (location = 0) in vec4 a_position;\n"
|
||||
"layout (location = 1) in attribute vec2 a_texcoord0;\n"
|
||||
"layout (location = 0) out vec2 v_texcoord0;\n"
|
||||
"void main() {\n"
|
||||
" v_texcoord0 = a_texcoord0;\n"
|
||||
" gl_Position = a_position;\n"
|
||||
"}\n";
|
||||
static const char tex_vs[] = R"(#version 400
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
layout (location = 0) in vec3 a_position;
|
||||
layout (location = 1) in vec2 a_texcoord0;
|
||||
layout (location = 0) out vec2 v_texcoord0;
|
||||
void main() {
|
||||
v_texcoord0 = a_texcoord0;
|
||||
gl_Position = vec4(a_position, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
void ConvertFromRGBA8888_Vulkan(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format);
|
||||
|
||||
void FramebufferManagerVulkan::ClearBuffer(bool keepState) {
|
||||
// keepState is irrelevant.
|
||||
if (!currentRenderVfb_) {
|
||||
return;
|
||||
FramebufferManagerVulkan::FramebufferManagerVulkan(VulkanContext *vulkan) :
|
||||
vulkan_(vulkan),
|
||||
drawPixelsTex_(nullptr),
|
||||
drawPixelsTexFormat_(GE_FORMAT_INVALID),
|
||||
convBuf_(nullptr),
|
||||
convBufSize_(0),
|
||||
textureCache_(nullptr),
|
||||
shaderManager_(nullptr),
|
||||
resized_(false),
|
||||
pixelBufObj_(nullptr),
|
||||
currentPBO_(0),
|
||||
curFrame_(0),
|
||||
pipelineBasicTex_(VK_NULL_HANDLE),
|
||||
pipelinePostShader_(VK_NULL_HANDLE),
|
||||
vulkan2D_(vulkan) {
|
||||
|
||||
// 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.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
rp.pNext = NULL;
|
||||
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 };
|
||||
cp.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
cp.queueFamilyIndex = vulkan_->GetGraphicsQueueFamilyIndex();
|
||||
VkResult res = vkCreateCommandPool(vulkan_->GetDevice(), &cp, nullptr, &frameData_[i].cmdPool_);
|
||||
assert(res == VK_SUCCESS);
|
||||
frameData_[i].push_ = new VulkanPushBuffer(vulkan_, 64 * 1024);
|
||||
}
|
||||
VkClearAttachment clear[2];
|
||||
memset(clear, 0, sizeof(clear));
|
||||
clear[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
clear[1].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
VkClearRect rc;
|
||||
rc.baseArrayLayer = 0;
|
||||
rc.layerCount = 1;
|
||||
rc.rect.offset.x = 0;
|
||||
rc.rect.offset.y = 0;
|
||||
rc.rect.extent.width = currentRenderVfb_->bufferWidth;
|
||||
rc.rect.extent.height = currentRenderVfb_->bufferHeight;
|
||||
vkCmdClearAttachments(curCmd_, 2, clear, 1, &rc);
|
||||
|
||||
pipelineCache2D_ = vulkan_->CreatePipelineCache();
|
||||
|
||||
std::string fs_errors, vs_errors;
|
||||
fsBasicTex_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, tex_fs, &fs_errors);
|
||||
vsBasicTex_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, tex_vs, &vs_errors);
|
||||
assert(fsBasicTex_ != VK_NULL_HANDLE);
|
||||
assert(vsBasicTex_ != VK_NULL_HANDLE);
|
||||
|
||||
pipelineBasicTex_ = vulkan2D_.GetPipeline(pipelineCache2D_, rpClearColorClearDepth_, vsBasicTex_, fsBasicTex_);
|
||||
|
||||
VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
|
||||
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samp.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samp.magFilter = VK_FILTER_NEAREST;
|
||||
samp.minFilter = VK_FILTER_NEAREST;
|
||||
VkResult res = vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &nearestSampler_);
|
||||
assert(res == VK_SUCCESS);
|
||||
samp.magFilter = VK_FILTER_LINEAR;
|
||||
samp.minFilter = VK_FILTER_LINEAR;
|
||||
res = vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &linearSampler_);
|
||||
assert(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DisableState() {
|
||||
FramebufferManagerVulkan::~FramebufferManagerVulkan() {
|
||||
vulkan_->Delete().QueueDeleteRenderPass(rpLoadColorLoadDepth_);
|
||||
vulkan_->Delete().QueueDeleteRenderPass(rpClearColorLoadDepth_);
|
||||
vulkan_->Delete().QueueDeleteRenderPass(rpClearColorClearDepth_);
|
||||
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_);
|
||||
}
|
||||
vkDestroyCommandPool(vulkan_->GetDevice(), frameData_[i].cmdPool_, nullptr);
|
||||
frameData_[i].push_->Destroy(vulkan_);
|
||||
delete frameData_[i].push_;
|
||||
}
|
||||
delete drawPixelsTex_;
|
||||
|
||||
vulkan_->Delete().QueueDeleteShaderModule(fsBasicTex_);
|
||||
vulkan_->Delete().QueueDeleteShaderModule(vsBasicTex_);
|
||||
|
||||
vulkan_->Delete().QueueDeleteSampler(linearSampler_);
|
||||
vulkan_->Delete().QueueDeleteSampler(nearestSampler_);
|
||||
vulkan_->Delete().QueueDeletePipeline(pipelineBasicTex_);
|
||||
if (pipelinePostShader_ != VK_NULL_HANDLE)
|
||||
vulkan_->Delete().QueueDeletePipeline(pipelinePostShader_);
|
||||
vulkan_->Delete().QueueDeletePipelineCache(pipelineCache2D_);
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth) {
|
||||
if (!useBufferedRendering_) {
|
||||
float x, y, w, h;
|
||||
CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL);
|
||||
|
||||
VkClearValue colorValue, depthValue;
|
||||
colorValue.color.float32[0] = (color & 0xFF) * (1.0f / 255.0f);
|
||||
colorValue.color.float32[1] = ((color >> 8) & 0xFF) * (1.0f / 255.0f);
|
||||
colorValue.color.float32[2] = ((color >> 16) & 0xFF) * (1.0f / 255.0f);
|
||||
colorValue.color.float32[3] = ((color >> 24) & 0xFF) * (1.0f / 255.0f);
|
||||
depthValue.depthStencil.depth = depth;
|
||||
depthValue.depthStencil.stencil = (color >> 24) & 0xFF;
|
||||
|
||||
VkClearRect rect;
|
||||
rect.baseArrayLayer = 0;
|
||||
rect.layerCount = 1;
|
||||
rect.rect.offset.x = x;
|
||||
rect.rect.offset.y = y;
|
||||
rect.rect.extent.width = w;
|
||||
rect.rect.extent.height = h;
|
||||
|
||||
int count = 0;
|
||||
VkClearAttachment attach[2];
|
||||
// The Clear detection takes care of doing a regular draw instead if separate masking
|
||||
// of color and alpha is needed, so we can just treat them as the same.
|
||||
if (clearColor || clearAlpha) {
|
||||
attach[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
attach[count].clearValue = colorValue;
|
||||
attach[count].colorAttachment = 0;
|
||||
count++;
|
||||
}
|
||||
if (clearDepth) {
|
||||
attach[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
attach[count].clearValue = depthValue;
|
||||
attach[count].colorAttachment = 0;
|
||||
count++;
|
||||
}
|
||||
vkCmdClearAttachments(curCmd_, count, attach, 1, &rect);
|
||||
|
||||
if (clearColor || clearAlpha) {
|
||||
SetColorUpdated(gstate_c.skipDrawReason);
|
||||
}
|
||||
if (clearDepth) {
|
||||
SetDepthUpdated();
|
||||
}
|
||||
} else {
|
||||
// TODO: Clever render pass magic.
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DoNotifyDraw() {
|
||||
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight) {
|
||||
@ -115,44 +297,13 @@ void FramebufferManagerVulkan::UpdatePostShaderUniforms(int bufferWidth, int buf
|
||||
memcpy(postUniforms_.time, time, 4 * sizeof(float));
|
||||
}
|
||||
|
||||
FramebufferManagerVulkan::FramebufferManagerVulkan(VulkanContext *vulkan) :
|
||||
vulkan_(vulkan),
|
||||
drawPixelsTex_(0),
|
||||
drawPixelsTexFormat_(GE_FORMAT_INVALID),
|
||||
convBuf_(nullptr),
|
||||
textureCache_(nullptr),
|
||||
shaderManager_(nullptr),
|
||||
resized_(false),
|
||||
pixelBufObj_(nullptr),
|
||||
currentPBO_(0) {
|
||||
}
|
||||
|
||||
FramebufferManagerVulkan::~FramebufferManagerVulkan() {
|
||||
/*
|
||||
if (drawPixelsTex_)
|
||||
glDeleteTextures(1, &drawPixelsTex_);
|
||||
DestroyDraw2DProgram();
|
||||
if (stencilUploadProgram_) {
|
||||
glsl_destroy(stencilUploadProgram_);
|
||||
}
|
||||
SetNumExtraFBOs(0);
|
||||
|
||||
for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) {
|
||||
fbo_destroy(it->second.fbo_vk);
|
||||
}
|
||||
|
||||
delete[] pixelBufObj_;
|
||||
delete[] convBuf_;
|
||||
*/
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::Init() {
|
||||
FramebufferManagerCommon::Init();
|
||||
// Workaround for upscaling shaders where we force x1 resolution without saving it
|
||||
resized_ = true;
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) {
|
||||
VulkanTexture *FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) {
|
||||
if (drawPixelsTex_ && (drawPixelsTexFormat_ != srcPixelFormat || drawPixelsTex_->GetWidth() != width || drawPixelsTex_->GetHeight() != height)) {
|
||||
delete drawPixelsTex_;
|
||||
drawPixelsTex_ = nullptr;
|
||||
@ -167,15 +318,15 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor
|
||||
|
||||
// TODO: We can just change the texture format and flip some bits around instead of this.
|
||||
// Could share code with the texture cache perhaps.
|
||||
bool useConvBuf = false;
|
||||
const uint8_t *data = srcPixels;
|
||||
if (srcPixelFormat != GE_FORMAT_8888 || srcStride != width) {
|
||||
useConvBuf = true;
|
||||
u32 neededSize = width * height * 4;
|
||||
if (!convBuf_ || convBufSize_ < neededSize) {
|
||||
delete[] convBuf_;
|
||||
convBuf_ = new u8[neededSize];
|
||||
convBufSize_ = neededSize;
|
||||
}
|
||||
data = convBuf_;
|
||||
for (int y = 0; y < height; y++) {
|
||||
switch (srcPixelFormat) {
|
||||
case GE_FORMAT_565:
|
||||
@ -216,41 +367,38 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*VkBuffer buffer;
|
||||
size_t offset = drawEngine_->
|
||||
drawPixelsTex_->UploadMip(0, width, height, )*/
|
||||
|
||||
VkBuffer buffer;
|
||||
size_t offset = frameData_[curFrame_].push_->Push(data, width * height * 4, &buffer);
|
||||
drawPixelsTex_->UploadMip(0, width, height, buffer, (uint32_t)offset, width);
|
||||
drawPixelsTex_->EndCreate();
|
||||
return drawPixelsTex_;
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) {
|
||||
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height);
|
||||
/*
|
||||
float v0 = 0.0f, v1 = 1.0f;
|
||||
VkViewport vp;
|
||||
vp.minDepth = 0.0;
|
||||
vp.maxDepth = 1.0;
|
||||
if (useBufferedRendering_ && vfb && vfb->fbo_vk) {
|
||||
fbo_bind_as_render_target(vfb->fbo_vk);
|
||||
glViewport(0, 0, vfb->renderWidth, vfb->renderHeight);
|
||||
vp.x = 0;
|
||||
vp.y = 0;
|
||||
vp.width = vfb->renderWidth;
|
||||
vp.height = vfb->renderHeight;
|
||||
} else {
|
||||
// We are drawing to the back buffer so need to flip.
|
||||
v0 = 1.0f;
|
||||
v1 = 0.0f;
|
||||
float x, y, w, h;
|
||||
CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL);
|
||||
glViewport(x, y, w, h);
|
||||
CenterDisplayOutputRect(&vp.x, &vp.y, &vp.width, &vp.height, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL);
|
||||
}
|
||||
// TODO: Don't use the viewport mechanism for this.
|
||||
vkCmdSetViewport(curCmd_, 0, 1, &vp);
|
||||
|
||||
DrawActiveTexture(0, dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, 0.0f, v0, 1.0f, v1, nullptr, ROTATION_LOCKED_HORIZONTAL);
|
||||
VulkanTexture *pixelTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height);
|
||||
DrawTexture(pixelTex, dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, 0.0f, 0.0f, 1.0f, 1.0f, pipelineBasicTex_, ROTATION_LOCKED_HORIZONTAL);
|
||||
textureCache_->ForgetLastTexture();
|
||||
*/
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DrawFramebufferToOutput(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader) {
|
||||
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272);
|
||||
VulkanTexture *pixelTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, g_Config.iTexFiltering == TEX_FILTER_NEAREST ? GL_NEAREST : GL_LINEAR);
|
||||
|
||||
struct CardboardSettings cardboardSettings;
|
||||
GetCardboardSettings(&cardboardSettings);
|
||||
|
||||
// This might draw directly at the backbuffer (if so, applyPostShader is set) so if there's a post shader, we need to apply it here.
|
||||
// Should try to unify this path with the regular path somehow, but this simple solution works for most of the post shaders
|
||||
// (it always runs at output resolution so FXAA may look odd).
|
||||
@ -266,40 +414,56 @@ void FramebufferManagerVulkan::DrawFramebufferToOutput(const u8 *srcPixels, GEBu
|
||||
float u0 = 0.0f, u1 = 480.0f / 512.0f;
|
||||
float v0 = 0.0f, v1 = 1.0f;
|
||||
|
||||
// We are drawing directly to the back buffer.
|
||||
std::swap(v0, v1);
|
||||
|
||||
VkPipeline postShaderProgram_ = VK_NULL_HANDLE;
|
||||
|
||||
struct CardboardSettings cardboardSettings;
|
||||
GetCardboardSettings(&cardboardSettings);
|
||||
|
||||
// TODO: Don't use the viewport mechanism for this.
|
||||
VkViewport vp;
|
||||
vp.minDepth = 0.0f;
|
||||
vp.maxDepth = 1.0f;
|
||||
if (cardboardSettings.enabled) {
|
||||
// Left Eye Image
|
||||
// glstate.viewport.set(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight);
|
||||
vp.x = cardboardSettings.leftEyeXPosition;
|
||||
vp.y = cardboardSettings.screenYPosition;
|
||||
vp.width = cardboardSettings.screenWidth;
|
||||
vp.height = cardboardSettings.screenHeight;
|
||||
vkCmdSetViewport(curCmd_, 0, 1, &vp);
|
||||
if (applyPostShader && usePostShader_ && useBufferedRendering_) {
|
||||
DrawActiveTexture(0, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, postShaderProgram_, ROTATION_LOCKED_HORIZONTAL);
|
||||
DrawTexture(pixelTex, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, postShaderProgram_, ROTATION_LOCKED_HORIZONTAL);
|
||||
} else {
|
||||
DrawActiveTexture(0, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, VK_NULL_HANDLE, ROTATION_LOCKED_HORIZONTAL);
|
||||
DrawTexture(pixelTex, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, pipelineBasicTex_, ROTATION_LOCKED_HORIZONTAL);
|
||||
}
|
||||
|
||||
// Right Eye Image
|
||||
// glstate.viewport.set(cardboardSettings.rightEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight);
|
||||
vp.x = cardboardSettings.rightEyeXPosition;
|
||||
vkCmdSetViewport(curCmd_, 0, 1, &vp);
|
||||
if (applyPostShader && usePostShader_ && useBufferedRendering_) {
|
||||
DrawActiveTexture(0, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, postShaderProgram_, ROTATION_LOCKED_HORIZONTAL);
|
||||
DrawTexture(pixelTex, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, postShaderProgram_, ROTATION_LOCKED_HORIZONTAL);
|
||||
} else {
|
||||
DrawActiveTexture(0, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, VK_NULL_HANDLE, ROTATION_LOCKED_HORIZONTAL);
|
||||
DrawTexture(pixelTex, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, pipelineBasicTex_, ROTATION_LOCKED_HORIZONTAL);
|
||||
}
|
||||
} else {
|
||||
// Fullscreen Image
|
||||
// glstate.viewport.set(0, 0, pixelWidth_, pixelHeight_);
|
||||
vp.x = 0.0f;
|
||||
vp.y = 0.0f;
|
||||
vp.width = pixelWidth_;
|
||||
vp.height = pixelHeight_;
|
||||
vkCmdSetViewport(curCmd_, 0, 1, &vp);
|
||||
if (applyPostShader && usePostShader_ && useBufferedRendering_) {
|
||||
DrawActiveTexture(0, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, postShaderProgram_, uvRotation);
|
||||
DrawTexture(pixelTex, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, postShaderProgram_, uvRotation);
|
||||
} else {
|
||||
DrawActiveTexture(0, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, VK_NULL_HANDLE, uvRotation);
|
||||
DrawTexture(pixelTex, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, pipelineBasicTex_, uvRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive.
|
||||
void FramebufferManagerVulkan::DrawActiveTexture(VulkanTexture *texture, float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, VkPipeline pipeline, int uvRotation) {
|
||||
void FramebufferManagerVulkan::DrawTexture(VulkanTexture *texture, float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, VkPipeline pipeline, int uvRotation) {
|
||||
if (!texture)
|
||||
return;
|
||||
|
||||
float texCoords[8] = {
|
||||
u0,v0,
|
||||
u1,v0,
|
||||
@ -307,8 +471,6 @@ void FramebufferManagerVulkan::DrawActiveTexture(VulkanTexture *texture, float x
|
||||
u0,v1,
|
||||
};
|
||||
|
||||
static const uint16_t indices[4] = { 0,1,3,2 };
|
||||
|
||||
if (uvRotation != ROTATION_LOCKED_HORIZONTAL) {
|
||||
float temp[8];
|
||||
int rotation = 0;
|
||||
@ -323,60 +485,31 @@ void FramebufferManagerVulkan::DrawActiveTexture(VulkanTexture *texture, float x
|
||||
memcpy(texCoords, temp, sizeof(temp));
|
||||
}
|
||||
|
||||
if (texture) {
|
||||
// Previously had NVDrawTexture fallback here but wasn't worth it.
|
||||
// glBindTexture(GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
float pos[12] = {
|
||||
x,y,0,
|
||||
x + w,y,0,
|
||||
x + w,y + h,0,
|
||||
x,y + h,0
|
||||
Vulkan2D::Vertex vtx[4] = {
|
||||
{x,y,0,texCoords[0],texCoords[1]},
|
||||
{x + w,y,0,texCoords[2],texCoords[3]},
|
||||
{x,y + h,0,texCoords[6],texCoords[7] },
|
||||
{x + w,y + h,0,texCoords[4],texCoords[5] },
|
||||
};
|
||||
|
||||
float invDestW = 1.0f / (destW * 0.5f);
|
||||
float invDestH = 1.0f / (destH * 0.5f);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pos[i * 3] = pos[i * 3] * invDestW - 1.0f;
|
||||
pos[i * 3 + 1] = pos[i * 3 + 1] * invDestH - 1.0f;
|
||||
vtx[i].x = vtx[i].x * invDestW - 1.0f;
|
||||
vtx[i].y = vtx[i].y * invDestH - 1.0f;
|
||||
}
|
||||
|
||||
//if (!program) {
|
||||
// program = draw2dprogram_;
|
||||
//}
|
||||
VulkanPushBuffer *push = frameData_[curFrame_].push_;
|
||||
|
||||
// Upscaling postshaders doesn't look well with linear
|
||||
/*
|
||||
if (postShaderIsUpscalingFilter_) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
} else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, g_Config.iBufFilter == SCALE_NEAREST ? GL_NEAREST : GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, g_Config.iBufFilter == SCALE_NEAREST ? GL_NEAREST : GL_LINEAR);
|
||||
}
|
||||
*/
|
||||
VkCommandBuffer cmd = curCmd_;
|
||||
|
||||
//if (program != postShaderProgram_) {
|
||||
// shaderManager_->DirtyLastShader(); // dirty lastShader_
|
||||
// glsl_bind(program);
|
||||
//}
|
||||
|
||||
/*
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_VAO)) {
|
||||
transformDraw_->BindBuffer(pos, sizeof(pos), texCoords, sizeof(texCoords));
|
||||
transformDraw_->BindElementBuffer(indices, sizeof(indices));
|
||||
glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0);
|
||||
glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (void *)sizeof(pos));
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, 0);
|
||||
} else {
|
||||
glstate.arrayBuffer.unbind();
|
||||
glstate.elementArrayBuffer.unbind();
|
||||
glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos);
|
||||
glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, texCoords);
|
||||
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);
|
||||
}
|
||||
*/
|
||||
// TODO: Choose linear or nearest appropriately, see GL impl.
|
||||
vulkan2D_.BindDescriptorSet(cmd, texture->GetImageView(), linearSampler_);
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
VkBuffer vbuffer;
|
||||
VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer);
|
||||
vkCmdBindVertexBuffers(cmd, 0, 1, &vbuffer, &offset);
|
||||
vkCmdDraw(cmd, 4, 1, 0, 0);
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DestroyFramebuf(VirtualFramebuffer *v) {
|
||||
@ -404,6 +537,9 @@ void FramebufferManagerVulkan::RebindFramebuffer() {
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w, u16 h, bool force) {
|
||||
return;
|
||||
|
||||
/*
|
||||
VirtualFramebuffer old = *vfb;
|
||||
|
||||
if (force) {
|
||||
@ -476,6 +612,7 @@ void FramebufferManagerVulkan::ResizeFramebufFBO(VirtualFramebuffer *vfb, u16 w,
|
||||
if (!vfb->fbo_vk) {
|
||||
ERROR_LOG(SCEGE, "Error creating FBO! %i x %i", vfb->renderWidth, vfb->renderHeight);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::NotifyRenderFramebufferCreated(VirtualFramebuffer *vfb) {
|
||||
@ -485,7 +622,6 @@ void FramebufferManagerVulkan::NotifyRenderFramebufferCreated(VirtualFramebuffer
|
||||
}
|
||||
|
||||
textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_CREATED);
|
||||
|
||||
// ugly...
|
||||
if ((gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) && shaderManager_) {
|
||||
shaderManager_->DirtyUniform(DIRTY_PROJMATRIX);
|
||||
@ -613,39 +749,16 @@ void FramebufferManagerVulkan::BlitFramebufferDepth(VirtualFramebuffer *src, Vir
|
||||
}
|
||||
}
|
||||
|
||||
VulkanFBO *FramebufferManagerVulkan::GetTempFBO(u16 w, u16 h, VulkanFBOColorDepth depth) {
|
||||
u64 key = ((u64)depth << 32) | ((u32)w << 16) | h;
|
||||
auto it = tempFBOs_.find(key);
|
||||
if (it != tempFBOs_.end()) {
|
||||
it->second.last_frame_used = gpuStats.numFlips;
|
||||
return it->second.fbo_vk;
|
||||
}
|
||||
|
||||
textureCache_->ForgetLastTexture();
|
||||
// FBO *fbo_vk = fbo_create(w, h, 1, false, depth);
|
||||
VulkanFBO *fbo_vk = new VulkanFBO();
|
||||
if (!fbo_vk)
|
||||
return nullptr;
|
||||
// fbo_bind_as_render_target(fbo_vk);
|
||||
// fbo_vk->GetColorImageView()
|
||||
// ClearBuffer(true);
|
||||
const TempFBO info = { fbo_vk, gpuStats.numFlips };
|
||||
tempFBOs_[key] = info;
|
||||
return fbo_vk;
|
||||
}
|
||||
|
||||
// This function signature will need to change. Only used for shader blending, doing it later.
|
||||
void FramebufferManagerVulkan::BindFramebufferColor(int stage, u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags) {
|
||||
VulkanTexture *FramebufferManagerVulkan::GetFramebufferColor(u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags) {
|
||||
if (framebuffer == NULL) {
|
||||
framebuffer = currentRenderVfb_;
|
||||
}
|
||||
|
||||
if (!framebuffer->fbo_vk || !useBufferedRendering_) {
|
||||
gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE;
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
// currentRenderVfb_ will always be set when this is called, except from the GE debugger.
|
||||
// Let's just not bother with the copy in that case.
|
||||
bool skipCopy = (flags & BINDFBCOLOR_MAY_COPY) == 0;
|
||||
@ -653,8 +766,11 @@ void FramebufferManagerVulkan::BindFramebufferColor(int stage, u32 fbRawAddress,
|
||||
skipCopy = true;
|
||||
}
|
||||
if (!skipCopy && currentRenderVfb_ && framebuffer->fb_address == fbRawAddress) {
|
||||
// TODO: Enable the below code
|
||||
return framebuffer->fbo_vk->GetColor();
|
||||
/*
|
||||
// TODO: Maybe merge with bvfbs_? Not sure if those could be packing, and they're created at a different size.
|
||||
fbo_vk *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (FBOColorDepth)framebuffer->colorDepth);
|
||||
VulkanFBO *renderCopy = GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, (FBOColorDepth)framebuffer->colorDepth);
|
||||
if (renderCopy) {
|
||||
VirtualFramebuffer copyInfo = *framebuffer;
|
||||
copyInfo.fbo_vk = renderCopy;
|
||||
@ -681,44 +797,19 @@ void FramebufferManagerVulkan::BindFramebufferColor(int stage, u32 fbRawAddress,
|
||||
|
||||
BlitFramebuffer(©Info, x, y, framebuffer, x, y, w, h, 0);
|
||||
|
||||
fbo_bind_color_as_texture(renderCopy, 0);
|
||||
return nullptr; // fbo_bind_color_as_texture(renderCopy, 0);
|
||||
} else {
|
||||
fbo_bind_color_as_texture(framebuffer->fbo_vk, 0);
|
||||
return framebuffer->fbo_vk->GetColor();
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
fbo_bind_color_as_texture(framebuffer->fbo_vk, 0);
|
||||
return framebuffer->fbo_vk->GetColor();
|
||||
}
|
||||
|
||||
if (stage != GL_TEXTURE0) {
|
||||
glActiveTexture(stage);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
struct CardboardSettings * FramebufferManagerVulkan::GetCardboardSettings(struct CardboardSettings * cardboardSettings) {
|
||||
if (cardboardSettings) {
|
||||
// Calculate Cardboard Settings
|
||||
float cardboardScreenScale = g_Config.iCardboardScreenSize / 100.0f;
|
||||
float cardboardScreenWidth = pixelWidth_ / 2.0f * cardboardScreenScale;
|
||||
float cardboardScreenHeight = pixelHeight_ / 2.0f * cardboardScreenScale;
|
||||
float cardboardMaxXShift = (pixelWidth_ / 2.0f - cardboardScreenWidth) / 2.0f;
|
||||
float cardboardUserXShift = g_Config.iCardboardXShift / 100.0f * cardboardMaxXShift;
|
||||
float cardboardLeftEyeX = cardboardMaxXShift + cardboardUserXShift;
|
||||
float cardboardRightEyeX = pixelWidth_ / 2.0f + cardboardMaxXShift - cardboardUserXShift;
|
||||
float cardboardMaxYShift = pixelHeight_ / 2.0f - cardboardScreenHeight / 2.0f;
|
||||
float cardboardUserYShift = g_Config.iCardboardYShift / 100.0f * cardboardMaxYShift;
|
||||
float cardboardScreenY = cardboardMaxYShift + cardboardUserYShift;
|
||||
|
||||
// Copy current Settings into Structure
|
||||
cardboardSettings->enabled = g_Config.bEnableCardboard;
|
||||
cardboardSettings->leftEyeXPosition = cardboardLeftEyeX;
|
||||
cardboardSettings->rightEyeXPosition = cardboardRightEyeX;
|
||||
cardboardSettings->screenYPosition = cardboardScreenY;
|
||||
cardboardSettings->screenWidth = cardboardScreenWidth;
|
||||
cardboardSettings->screenHeight = cardboardScreenHeight;
|
||||
}
|
||||
|
||||
return cardboardSettings;
|
||||
cardboardSettings->enabled = false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::CopyDisplayToOutput() {
|
||||
@ -732,7 +823,6 @@ void FramebufferManagerVulkan::CopyDisplayToOutput() {
|
||||
// is worth the needed refactoring trouble though.
|
||||
|
||||
// fbo_unbind();
|
||||
// glstate.viewport.set(0, 0, pixelWidth_, pixelHeight_);
|
||||
|
||||
currentRenderVfb_ = 0;
|
||||
|
||||
@ -750,12 +840,10 @@ void FramebufferManagerVulkan::CopyDisplayToOutput() {
|
||||
u32 offsetX = 0;
|
||||
u32 offsetY = 0;
|
||||
|
||||
struct CardboardSettings cardboardSettings;
|
||||
GetCardboardSettings(&cardboardSettings);
|
||||
|
||||
VirtualFramebuffer *vfb = GetVFBAt(displayFramebufPtr_);
|
||||
if (!vfb) {
|
||||
// Let's search for a framebuf within this range.
|
||||
// TODO: Let's keep an interval_map or something of the memory contents.
|
||||
const u32 addr = (displayFramebufPtr_ & 0x03FFFFFF) | 0x04000000;
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *v = vfbs_[i];
|
||||
@ -831,11 +919,15 @@ void FramebufferManagerVulkan::CopyDisplayToOutput() {
|
||||
displayFramebuf_ = vfb;
|
||||
|
||||
if (vfb->fbo_vk) {
|
||||
struct CardboardSettings cardboardSettings;
|
||||
GetCardboardSettings(&cardboardSettings);
|
||||
|
||||
DEBUG_LOG(SCEGE, "Displaying FBO %08x", vfb->fb_address);
|
||||
|
||||
// We should not be in a renderpass here so can just copy.
|
||||
|
||||
// GLuint colorTexture = fbo_get_color_texture(vfb->fbo_vk);
|
||||
VulkanTexture *colorTexture = vfb->fbo_vk->GetColor();
|
||||
|
||||
int uvRotation = (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE) ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL;
|
||||
|
||||
@ -850,19 +942,32 @@ void FramebufferManagerVulkan::CopyDisplayToOutput() {
|
||||
float u1 = (480.0f + offsetX) / (float)vfb->bufferWidth;
|
||||
float v1 = (272.0f + offsetY) / (float)vfb->bufferHeight;
|
||||
|
||||
VkViewport vp;
|
||||
vp.minDepth = 0.0f;
|
||||
vp.maxDepth = 1.0f;
|
||||
|
||||
if (!usePostShader_) {
|
||||
if (cardboardSettings.enabled) {
|
||||
// Left Eye Image
|
||||
// glstate.viewport.set(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight);
|
||||
// DrawActiveTexture(colorTexture, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, nullptr, ROTATION_LOCKED_HORIZONTAL);
|
||||
vp.x = cardboardSettings.leftEyeXPosition;
|
||||
vp.y = cardboardSettings.screenYPosition;
|
||||
vp.width = cardboardSettings.screenWidth;
|
||||
vp.height = cardboardSettings.screenHeight;
|
||||
vkCmdSetViewport(curCmd_, 0, 1, &vp);
|
||||
DrawTexture(colorTexture, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, pipelineBasicTex_, ROTATION_LOCKED_HORIZONTAL);
|
||||
|
||||
// Right Eye Image
|
||||
// glstate.viewport.set(cardboardSettings.rightEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight);
|
||||
// DrawActiveTexture(colorTexture, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, nullptr, ROTATION_LOCKED_HORIZONTAL);
|
||||
vp.x = cardboardSettings.rightEyeXPosition;
|
||||
vkCmdSetViewport(curCmd_, 0, 1, &vp);
|
||||
DrawTexture(colorTexture, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, pipelineBasicTex_, ROTATION_LOCKED_HORIZONTAL);
|
||||
} else {
|
||||
// Fullscreen Image
|
||||
// glstate.viewport.set(0, 0, pixelWidth_, pixelHeight_);
|
||||
// DrawActiveTexture(colorTexture, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, nullptr, uvRotation);
|
||||
vp.x = 0.0f;
|
||||
vp.y = 0.0f;
|
||||
vp.width = pixelWidth_;
|
||||
vp.height = pixelHeight_;
|
||||
vkCmdSetViewport(curCmd_, 0, 1, &vp);
|
||||
DrawTexture(colorTexture, x, y, w, h, (float)pixelWidth_, (float)pixelHeight_, u0, v0, u1, v1, pipelineBasicTex_, uvRotation);
|
||||
}
|
||||
} else if (usePostShader_ && !postShaderAtOutputResolution_) {
|
||||
// An additional pass, post-processing shader to the extra FBO.
|
||||
@ -1319,6 +1424,40 @@ void FramebufferManagerVulkan::PackFramebufferSync_(VirtualFramebuffer *vfb, int
|
||||
void ShowScreenResolution();
|
||||
#endif
|
||||
|
||||
VkCommandBuffer FramebufferManagerVulkan::AllocFrameCommandBuffer() {
|
||||
FrameData &frame = frameData_[curFrame_];
|
||||
int num = frame.numCommandBuffers_;
|
||||
if (!frame.commandBuffers_[num]) {
|
||||
VkCommandBufferAllocateInfo cmd = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
||||
cmd.commandBufferCount = 1;
|
||||
cmd.commandPool = frame.cmdPool_;
|
||||
cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
vkAllocateCommandBuffers(vulkan_->GetDevice(), &cmd, &frame.commandBuffers_[num]);
|
||||
frame.totalCommandBuffers_ = num + 1;
|
||||
}
|
||||
return frame.commandBuffers_[num];
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::BeginFrameVulkan() {
|
||||
BeginFrame();
|
||||
|
||||
FrameData &frame = frameData_[curFrame_];
|
||||
vkResetCommandPool(vulkan_->GetDevice(), frame.cmdPool_, 0);
|
||||
frame.numCommandBuffers_ = 0;
|
||||
|
||||
frame.push_->Reset();
|
||||
frame.push_->Begin(vulkan_);
|
||||
|
||||
if (!useBufferedRendering_) {
|
||||
// We only use a single command buffer in this case.
|
||||
curCmd_ = vulkan_->GetSurfaceCommandBuffer();
|
||||
VkRect2D scissor;
|
||||
scissor.offset = { 0, 0 };
|
||||
scissor.extent = { (uint32_t)pixelWidth_, (uint32_t)pixelHeight_ };
|
||||
vkCmdSetScissor(curCmd_, 0, 1, &scissor);
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::EndFrame() {
|
||||
if (resized_) {
|
||||
// TODO: Only do this if the new size actually changed the renderwidth/height.
|
||||
@ -1370,6 +1509,11 @@ void FramebufferManagerVulkan::EndFrame() {
|
||||
// Only do this in the read-framebuffer modes.
|
||||
if (updateVRAM_)
|
||||
PackFramebufferAsync_(nullptr);
|
||||
FrameData &frame = frameData_[curFrame_];
|
||||
frame.push_->End();
|
||||
|
||||
curFrame_++;
|
||||
curFrame_ &= 1;
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DeviceLost() {
|
||||
@ -1419,27 +1563,6 @@ void FramebufferManagerVulkan::DecimateFBOs() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = tempFBOs_.begin(); it != tempFBOs_.end(); ) {
|
||||
int age = frameLastFramebufUsed_ - it->second.last_frame_used;
|
||||
if (age > FBO_OLD_AGE) {
|
||||
delete it->second.fbo_vk;
|
||||
tempFBOs_.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same for ReadFramebuffersToMemory's VFBs
|
||||
for (size_t i = 0; i < bvfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *vfb = bvfbs_[i];
|
||||
int age = frameLastFramebufUsed_ - vfb->last_frame_render;
|
||||
if (age > FBO_OLD_AGE) {
|
||||
INFO_LOG(SCEGE, "Decimating FBO for %08x (%i x %i x %i), age %i", vfb->fb_address, vfb->width, vfb->height, vfb->format, age);
|
||||
DestroyFramebuf(vfb);
|
||||
bvfbs_.erase(bvfbs_.begin() + i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::DestroyAllFBOs() {
|
||||
@ -1460,11 +1583,6 @@ void FramebufferManagerVulkan::DestroyAllFBOs() {
|
||||
DestroyFramebuf(vfb);
|
||||
}
|
||||
bvfbs_.clear();
|
||||
|
||||
for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) {
|
||||
delete it->second.fbo_vk;
|
||||
}
|
||||
tempFBOs_.clear();
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::FlushBeforeCopy() {
|
||||
@ -1475,7 +1593,7 @@ void FramebufferManagerVulkan::FlushBeforeCopy() {
|
||||
// all the irrelevant state checking it'll use to decide what to do. Should
|
||||
// do something more focused here.
|
||||
SetRenderFrameBuffer(gstate_c.framebufChanged, gstate_c.skipDrawReason);
|
||||
transformDraw_->Flush(curCmd_);
|
||||
drawEngine_->Flush(curCmd_);
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::Resized() {
|
||||
@ -1570,3 +1688,23 @@ bool FramebufferManagerVulkan::GetStencilbuffer(u32 fb_address, int fb_stride, G
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void FramebufferManagerVulkan::ClearBuffer(bool keepState) {
|
||||
// keepState is irrelevant.
|
||||
if (!currentRenderVfb_) {
|
||||
return;
|
||||
}
|
||||
VkClearAttachment clear[2];
|
||||
memset(clear, 0, sizeof(clear));
|
||||
clear[0].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
clear[1].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
VkClearRect rc;
|
||||
rc.baseArrayLayer = 0;
|
||||
rc.layerCount = 1;
|
||||
rc.rect.offset.x = 0;
|
||||
rc.rect.offset.y = 0;
|
||||
rc.rect.extent.width = currentRenderVfb_->bufferWidth;
|
||||
rc.rect.extent.height = currentRenderVfb_->bufferHeight;
|
||||
vkCmdClearAttachments(curCmd_, 2, clear, 1, &rc);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "GPU/Common/GPUDebugInterface.h"
|
||||
#include "GPU/Vulkan/VulkanUtil.h"
|
||||
|
||||
// TODO: WTF?
|
||||
// TODO: Remove?
|
||||
enum VulkanFBOColorDepth {
|
||||
VK_FBO_8888,
|
||||
VK_FBO_565,
|
||||
@ -36,6 +36,7 @@ class DrawEngineVulkan;
|
||||
class VulkanContext;
|
||||
class ShaderManagerVulkan;
|
||||
class VulkanTexture;
|
||||
class VulkanPushBuffer;
|
||||
|
||||
struct PostShaderUniforms {
|
||||
float texelDelta[2]; float pad[2];
|
||||
@ -84,7 +85,7 @@ public:
|
||||
shaderManager_ = sm;
|
||||
}
|
||||
void SetDrawEngine(DrawEngineVulkan *td) {
|
||||
transformDraw_ = td;
|
||||
drawEngine_ = td;
|
||||
}
|
||||
|
||||
void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) override;
|
||||
@ -92,12 +93,15 @@ public:
|
||||
|
||||
// If texture != 0, will bind it.
|
||||
// x,y,w,h are relative to destW, destH which fill out the target completely.
|
||||
void DrawActiveTexture(VulkanTexture *texture, float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, VkPipeline pipeline, int uvRotation);
|
||||
void DrawTexture(VulkanTexture *texture, float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, VkPipeline pipeline, int uvRotation);
|
||||
|
||||
void DestroyAllFBOs();
|
||||
|
||||
virtual void Init() override;
|
||||
|
||||
void BeginFrameVulkan(); // there's a BeginFrame in the base class, which this calls
|
||||
void EndFrame();
|
||||
|
||||
void Resized();
|
||||
void DeviceLost();
|
||||
void CopyDisplayToOutput();
|
||||
@ -107,7 +111,7 @@ public:
|
||||
void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst);
|
||||
|
||||
// For use when texturing from a framebuffer. May create a duplicate if target.
|
||||
void BindFramebufferColor(int stage, u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags);
|
||||
VulkanTexture *GetFramebufferColor(u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags);
|
||||
|
||||
// Reads a rectangular subregion of a framebuffer to the right position in its backing memory.
|
||||
void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override;
|
||||
@ -127,13 +131,23 @@ public:
|
||||
|
||||
virtual void RebindFramebuffer() override;
|
||||
|
||||
VulkanFBO *GetTempFBO(u16 w, u16 h, VulkanFBOColorDepth depth = VK_FBO_8888);
|
||||
// VulkanFBO *GetTempFBO(u16 w, u16 h, VulkanFBOColorDepth depth = VK_FBO_8888);
|
||||
|
||||
// Cardboard Settings Calculator
|
||||
struct CardboardSettings * GetCardboardSettings(struct CardboardSettings * cardboardSettings);
|
||||
|
||||
// Pass management
|
||||
// void BeginPassClear()
|
||||
|
||||
// If within a render pass, this will just issue a regular clear. If beginning a new render pass,
|
||||
// do that.
|
||||
void NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth);
|
||||
void NotifyDraw() {
|
||||
DoNotifyDraw();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void DisableState() override;
|
||||
virtual void DisableState() override {}
|
||||
virtual void ClearBuffer(bool keepState = false);
|
||||
virtual void FlushBeforeCopy() override;
|
||||
virtual void DecimateFBOs() override;
|
||||
@ -149,7 +163,12 @@ protected:
|
||||
|
||||
|
||||
private:
|
||||
void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);
|
||||
|
||||
// The returned texture does not need to be free'd, might be returned from a pool (currently single entry)
|
||||
VulkanTexture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);
|
||||
void DoNotifyDraw();
|
||||
|
||||
VkCommandBuffer AllocFrameCommandBuffer();
|
||||
void UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight);
|
||||
|
||||
void PackFramebufferAsync_(VirtualFramebuffer *vfb);
|
||||
@ -161,8 +180,7 @@ private:
|
||||
// One framebuffer can be used as a texturing source at multiple times in a frame,
|
||||
// but then the contents have to be copied out into a new texture every time.
|
||||
VkCommandBuffer curCmd_;
|
||||
|
||||
DrawEngineVulkan *drawEngine_;
|
||||
VkCommandBuffer cmdInit_;
|
||||
|
||||
// Used by DrawPixels
|
||||
VulkanTexture *drawPixelsTex_;
|
||||
@ -173,21 +191,51 @@ private:
|
||||
|
||||
TextureCacheVulkan *textureCache_;
|
||||
ShaderManagerVulkan *shaderManager_;
|
||||
DrawEngineVulkan *transformDraw_;
|
||||
DrawEngineVulkan *drawEngine_;
|
||||
|
||||
bool resized_;
|
||||
|
||||
struct TempFBO {
|
||||
VulkanFBO *fbo_vk;
|
||||
int last_frame_used;
|
||||
AsyncPBOVulkan *pixelBufObj_;
|
||||
int currentPBO_;
|
||||
|
||||
enum {
|
||||
MAX_COMMAND_BUFFERS = 32,
|
||||
};
|
||||
|
||||
std::map<u64, TempFBO> tempFBOs_;
|
||||
struct FrameData {
|
||||
VkCommandPool cmdPool_;
|
||||
// Keep track of command buffers we allocated so we can reset or free them at an appropriate point.
|
||||
VkCommandBuffer commandBuffers_[MAX_COMMAND_BUFFERS];
|
||||
VulkanPushBuffer *push_;
|
||||
int numCommandBuffers_;
|
||||
int totalCommandBuffers_;
|
||||
};
|
||||
|
||||
// Not used under ES currently.
|
||||
AsyncPBOVulkan *pixelBufObj_; //this isn't that large
|
||||
u8 currentPBO_;
|
||||
FrameData frameData_[2];
|
||||
int curFrame_;
|
||||
|
||||
// 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_;
|
||||
|
||||
// Postprocessing
|
||||
VkPipeline pipelinePostShader_;
|
||||
|
||||
VkSampler linearSampler_;
|
||||
VkSampler nearestSampler_;
|
||||
|
||||
// Simple 2D drawing engine.
|
||||
Vulkan2D vulkan2D_;
|
||||
};
|
||||
|
@ -503,6 +503,11 @@ void GPU_Vulkan::BeginHostFrame() {
|
||||
textureCache_.StartFrame();
|
||||
depalShaderCache_.Decimate();
|
||||
|
||||
framebufferManager_->BeginFrameVulkan();
|
||||
|
||||
shaderManager_->DirtyShader();
|
||||
shaderManager_->DirtyUniform(DIRTY_ALL);
|
||||
|
||||
if (dumpNextFrame_) {
|
||||
NOTICE_LOG(G3D, "DUMPING THIS FRAME");
|
||||
dumpThisFrame_ = true;
|
||||
@ -510,11 +515,6 @@ void GPU_Vulkan::BeginHostFrame() {
|
||||
} else if (dumpThisFrame_) {
|
||||
dumpThisFrame_ = false;
|
||||
}
|
||||
|
||||
shaderManager_->DirtyShader();
|
||||
shaderManager_->DirtyUniform(DIRTY_ALL);
|
||||
|
||||
framebufferManager_->BeginFrame();
|
||||
}
|
||||
|
||||
void GPU_Vulkan::EndHostFrame() {
|
||||
@ -840,6 +840,7 @@ void GPU_Vulkan::Execute_Prim(u32 op, u32 diff) {
|
||||
|
||||
// This also makes skipping drawing very effective.
|
||||
framebufferManager_->SetRenderFrameBuffer(gstate_c.framebufChanged, gstate_c.skipDrawReason);
|
||||
|
||||
if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) {
|
||||
drawEngine_.SetupVertexDecoder(gstate.vertType);
|
||||
// Rough estimate, not sure what's correct.
|
||||
|
@ -54,7 +54,7 @@ static const DeclTypeInfo VComp[] = {
|
||||
{ VK_FORMAT_R16G16_UINT, "R16G16_UINT" }, // DEC_U16A_2,
|
||||
};
|
||||
|
||||
void VertexAttribSetup(VkVertexInputAttributeDescription *attr, int fmt, int offset, PspAttributeLocation location) {
|
||||
static void VertexAttribSetup(VkVertexInputAttributeDescription *attr, int fmt, int offset, PspAttributeLocation location) {
|
||||
attr->location = (uint32_t)location;
|
||||
attr->binding = 0;
|
||||
attr->format = VComp[fmt].type;
|
||||
@ -64,7 +64,7 @@ void VertexAttribSetup(VkVertexInputAttributeDescription *attr, int fmt, int off
|
||||
// Returns the number of attributes that were set.
|
||||
// We could cache these AttributeDescription arrays (with pspFmt as the key), but hardly worth bothering
|
||||
// as we will only call this code when we need to create a new VkPipeline.
|
||||
int SetupVertexAttribs(VkVertexInputAttributeDescription attrs[], const DecVtxFormat &decFmt) {
|
||||
static int SetupVertexAttribs(VkVertexInputAttributeDescription attrs[], const DecVtxFormat &decFmt) {
|
||||
int count = 0;
|
||||
if (decFmt.w0fmt != 0) {
|
||||
VertexAttribSetup(&attrs[count++], decFmt.w0fmt, decFmt.w0off, PspAttributeLocation::W1);
|
||||
@ -89,7 +89,7 @@ int SetupVertexAttribs(VkVertexInputAttributeDescription attrs[], const DecVtxFo
|
||||
return count;
|
||||
}
|
||||
|
||||
int SetupVertexAttribsPretransformed(VkVertexInputAttributeDescription attrs[], const DecVtxFormat &decFmt) {
|
||||
static int SetupVertexAttribsPretransformed(VkVertexInputAttributeDescription attrs[], const DecVtxFormat &decFmt) {
|
||||
int count = 0;
|
||||
VertexAttribSetup(&attrs[count++], DEC_FLOAT_4, 0, PspAttributeLocation::POSITION);
|
||||
VertexAttribSetup(&attrs[count++], DEC_FLOAT_3, 16, PspAttributeLocation::TEXCOORD);
|
||||
|
@ -748,7 +748,7 @@ void TextureCacheVulkan::ApplyTextureFramebuffer(VkCommandBuffer cmd, TexCacheEn
|
||||
}
|
||||
if (depal) {
|
||||
// VulkanTexture *clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_);
|
||||
VulkanFBO *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, VK_FBO_8888);
|
||||
// VulkanFBO *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, VK_FBO_8888);
|
||||
|
||||
//depalFBO->BeginPass(cmd);
|
||||
|
||||
@ -1288,6 +1288,8 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) {
|
||||
delete entry->vkTex;
|
||||
entry->vkTex = nullptr;
|
||||
}
|
||||
} else {
|
||||
// TODO: If reusing an existing texture object, we must transition it into the correct layout.
|
||||
}
|
||||
lastBoundTexture = entry->vkTex;
|
||||
|
||||
@ -1307,6 +1309,8 @@ void TextureCacheVulkan::SetTexture(VulkanPushBuffer *uploadBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
entry->vkTex->texture_->EndCreate();
|
||||
|
||||
gstate_c.textureFullAlpha = entry->GetAlphaStatus() == TexCacheEntry::STATUS_ALPHA_FULL;
|
||||
gstate_c.textureSimpleAlpha = entry->GetAlphaStatus() != TexCacheEntry::STATUS_ALPHA_UNKNOWN;
|
||||
|
||||
|
@ -15,6 +15,9 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Vulkan/VulkanContext.h"
|
||||
#include "GPU/Vulkan/VulkanUtil.h"
|
||||
|
||||
VulkanFBO::VulkanFBO() : color_(nullptr), depthStencil_(nullptr) {}
|
||||
@ -43,3 +46,284 @@ void VulkanFBO::Create(VulkanContext *vulkan, VkRenderPass rp_compatible, int wi
|
||||
|
||||
vkCreateFramebuffer(vulkan->GetDevice(), &fb, nullptr, &framebuffer_);
|
||||
}
|
||||
|
||||
Vulkan2D::Vulkan2D(VulkanContext *vulkan) : vulkan_(vulkan) {
|
||||
// All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated.
|
||||
VkDescriptorSetLayoutBinding bindings[2] = {};
|
||||
bindings[0].descriptorCount = 1;
|
||||
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
bindings[0].binding = 0;
|
||||
bindings[1].descriptorCount = 1;
|
||||
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
bindings[1].binding = 1;
|
||||
|
||||
VkDevice device = vulkan_->GetDevice();
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo dsl = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
dsl.bindingCount = 2;
|
||||
dsl.pBindings = bindings;
|
||||
VkResult res = vkCreateDescriptorSetLayout(device, &dsl, nullptr, &descriptorSetLayout_);
|
||||
assert(VK_SUCCESS == res);
|
||||
|
||||
VkDescriptorPoolSize dpTypes[1];
|
||||
dpTypes[0].descriptorCount = 200;
|
||||
dpTypes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
|
||||
VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||
dp.flags = 0; // Don't want to mess around with individually freeing these, let's go fixed each frame and zap the whole array. Might try the dynamic approach later.
|
||||
dp.maxSets = 200;
|
||||
dp.pPoolSizes = dpTypes;
|
||||
dp.poolSizeCount = ARRAY_SIZE(dpTypes);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
VkResult res = vkCreateDescriptorPool(vulkan_->GetDevice(), &dp, nullptr, &frameData_[i].descPool);
|
||||
assert(VK_SUCCESS == res);
|
||||
}
|
||||
|
||||
VkPushConstantRange push = {};
|
||||
push.offset = 0;
|
||||
push.size = 32;
|
||||
push.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
||||
VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
pl.pPushConstantRanges = &push;
|
||||
pl.pushConstantRangeCount = 1;
|
||||
pl.setLayoutCount = 1;
|
||||
pl.pSetLayouts = &descriptorSetLayout_;
|
||||
pl.flags = 0;
|
||||
res = vkCreatePipelineLayout(device, &pl, nullptr, &pipelineLayout_);
|
||||
assert(VK_SUCCESS == res);
|
||||
}
|
||||
|
||||
Vulkan2D::~Vulkan2D() {
|
||||
VkDevice device = vulkan_->GetDevice();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
vulkan_->Delete().QueueDeleteDescriptorPool(frameData_[i].descPool);
|
||||
}
|
||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayout_, nullptr);
|
||||
vkDestroyPipelineLayout(device, pipelineLayout_, nullptr);
|
||||
}
|
||||
|
||||
void Vulkan2D::BeginFrame() {
|
||||
FrameData &frame = frameData_[curFrame_];
|
||||
frame.descSets.clear();
|
||||
vkResetDescriptorPool(vulkan_->GetDevice(), frame.descPool, 0);
|
||||
}
|
||||
|
||||
void Vulkan2D::EndFrame() {
|
||||
curFrame_ = (curFrame_ + 1) & 1;
|
||||
}
|
||||
|
||||
VkDescriptorSet Vulkan2D::GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2) {
|
||||
DescriptorSetKey key;
|
||||
key.imageView[0] = tex1;
|
||||
key.imageView[1] = tex2;
|
||||
key.sampler[0] = sampler1;
|
||||
key.sampler[1] = sampler2;
|
||||
|
||||
FrameData *frame = &frameData_[curFrame_ & 1];
|
||||
auto iter = frame->descSets.find(key);
|
||||
if (iter != frame->descSets.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
// Didn't find one in the frame descriptor set cache, let's make a new one.
|
||||
// We wipe the cache on every frame.
|
||||
|
||||
VkDescriptorSet desc;
|
||||
VkDescriptorSetAllocateInfo descAlloc = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||
descAlloc.pSetLayouts = &descriptorSetLayout_;
|
||||
descAlloc.descriptorPool = frame->descPool;
|
||||
descAlloc.descriptorSetCount = 1;
|
||||
VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc);
|
||||
assert(result == VK_SUCCESS);
|
||||
|
||||
// We just don't write to the slots we don't care about.
|
||||
VkWriteDescriptorSet writes[2];
|
||||
memset(writes, 0, sizeof(writes));
|
||||
// Main and sub textures
|
||||
int n = 0;
|
||||
VkDescriptorImageInfo image1 = {};
|
||||
VkDescriptorImageInfo image2 = {};
|
||||
if (tex1) {
|
||||
// TODO: Also support LAYOUT_GENERAL to be able to texture from framebuffers without transitioning them?
|
||||
image1.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
image1.imageView = tex1;
|
||||
image1.sampler = sampler1;
|
||||
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[n].pNext = nullptr;
|
||||
writes[n].dstBinding = 0;
|
||||
writes[n].pImageInfo = &image1;
|
||||
writes[n].descriptorCount = 1;
|
||||
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
writes[n].dstSet = desc;
|
||||
n++;
|
||||
}
|
||||
if (tex2) {
|
||||
// TODO: Also support LAYOUT_GENERAL to be able to texture from framebuffers without transitioning them?
|
||||
image2.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
image2.imageView = tex2;
|
||||
image2.sampler = sampler2;
|
||||
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[n].pNext = nullptr;
|
||||
writes[n].dstBinding = 1;
|
||||
writes[n].pImageInfo = &image2;
|
||||
writes[n].descriptorCount = 1;
|
||||
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
writes[n].dstSet = desc;
|
||||
n++;
|
||||
}
|
||||
|
||||
vkUpdateDescriptorSets(vulkan_->GetDevice(), n, writes, 0, nullptr);
|
||||
|
||||
frame->descSets[key] = desc;
|
||||
return desc;
|
||||
}
|
||||
|
||||
VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShaderModule vs, VkShaderModule fs) {
|
||||
PipelineKey key;
|
||||
key.vs = vs;
|
||||
key.fs = fs;
|
||||
|
||||
auto iter = pipelines_.find(key);
|
||||
if (iter != pipelines_.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
VkPipelineColorBlendAttachmentState blend0 = {};
|
||||
blend0.blendEnable = false;
|
||||
blend0.colorWriteMask = 0xF;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo cbs = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
|
||||
cbs.pAttachments = &blend0;
|
||||
cbs.attachmentCount = 1;
|
||||
cbs.logicOpEnable = false;
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo dss = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
|
||||
dss.depthBoundsTestEnable = false;
|
||||
dss.stencilTestEnable = false;
|
||||
dss.depthTestEnable = false;
|
||||
|
||||
VkDynamicState dynamicStates[2];
|
||||
int numDyn = 0;
|
||||
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_SCISSOR;
|
||||
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_VIEWPORT;
|
||||
|
||||
VkPipelineDynamicStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
||||
ds.pDynamicStates = dynamicStates;
|
||||
ds.dynamicStateCount = numDyn;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rs = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
rs.lineWidth = 1.0f;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo ms = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
||||
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkPipelineShaderStageCreateInfo ss[2] = {};
|
||||
ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
ss[0].module = vs;
|
||||
ss[0].pName = "main";
|
||||
ss[0].flags = 0;
|
||||
ss[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
ss[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
ss[1].module = fs;
|
||||
ss[1].pName = "main";
|
||||
ss[1].flags = 0;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssembly = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
||||
inputAssembly.flags = 0;
|
||||
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
inputAssembly.primitiveRestartEnable = true;
|
||||
|
||||
VkVertexInputAttributeDescription attrs[2];
|
||||
int attributeCount = 2;
|
||||
attrs[0].binding = 0;
|
||||
attrs[0].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
attrs[0].location = 0;
|
||||
attrs[0].offset = 0;
|
||||
attrs[1].binding = 0;
|
||||
attrs[1].format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attrs[1].location = 1;
|
||||
attrs[1].offset = 12;
|
||||
int vertexStride = 12 + 8;
|
||||
|
||||
VkVertexInputBindingDescription ibd = {};
|
||||
ibd.binding = 0;
|
||||
ibd.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
ibd.stride = vertexStride;
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vis = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
||||
vis.vertexBindingDescriptionCount = 1;
|
||||
vis.pVertexBindingDescriptions = &ibd;
|
||||
vis.vertexAttributeDescriptionCount = attributeCount;
|
||||
vis.pVertexAttributeDescriptions = attrs;
|
||||
|
||||
VkPipelineViewportStateCreateInfo views = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
||||
views.viewportCount = 1;
|
||||
views.scissorCount = 1;
|
||||
views.pViewports = nullptr; // dynamic
|
||||
views.pScissors = nullptr; // dynamic
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipe = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
|
||||
pipe.flags = 0;
|
||||
pipe.stageCount = 2;
|
||||
pipe.pStages = ss;
|
||||
pipe.basePipelineIndex = 0;
|
||||
|
||||
pipe.pColorBlendState = &cbs;
|
||||
pipe.pDepthStencilState = &dss;
|
||||
pipe.pRasterizationState = &rs;
|
||||
|
||||
// We will use dynamic viewport state.
|
||||
pipe.pVertexInputState = &vis;
|
||||
pipe.pViewportState = &views;
|
||||
pipe.pTessellationState = nullptr;
|
||||
pipe.pDynamicState = &ds;
|
||||
pipe.pInputAssemblyState = &inputAssembly;
|
||||
pipe.pMultisampleState = &ms;
|
||||
pipe.layout = pipelineLayout_;
|
||||
pipe.basePipelineHandle = VK_NULL_HANDLE;
|
||||
pipe.basePipelineIndex = 0;
|
||||
pipe.renderPass = rp;
|
||||
pipe.subpass = 0;
|
||||
|
||||
VkPipeline pipeline;
|
||||
VkResult result = vkCreateGraphicsPipelines(vulkan_->GetDevice(), cache, 1, &pipe, nullptr, &pipeline);
|
||||
if (result == VK_SUCCESS) {
|
||||
pipelines_[key] = pipeline;
|
||||
return pipeline;
|
||||
} else {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void Vulkan2D::BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1) {
|
||||
VkDescriptorSet descSet = GetDescriptorSet(tex1, sampler1, VK_NULL_HANDLE, VK_NULL_HANDLE);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &descSet, 0, nullptr);
|
||||
}
|
||||
|
||||
VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *error) {
|
||||
std::vector<uint32_t> spirv;
|
||||
bool success = GLSLtoSPV(stage, code, spirv, error);
|
||||
if (!error->empty()) {
|
||||
if (success) {
|
||||
ERROR_LOG(G3D, "Warnings in shader compilation!");
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Error in shader compilation!");
|
||||
}
|
||||
ERROR_LOG(G3D, "Messages: %s", error->c_str());
|
||||
ERROR_LOG(G3D, "Shader source:\n%s", code);
|
||||
OutputDebugStringUTF8("Messages:\n");
|
||||
OutputDebugStringUTF8(error->c_str());
|
||||
return VK_NULL_HANDLE;
|
||||
} else {
|
||||
VkShaderModule module;
|
||||
if (vulkan->CreateShaderModule(spirv, &module)) {
|
||||
return module;
|
||||
} else {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <map>
|
||||
|
||||
#include "Common/Vulkan/VulkanContext.h"
|
||||
#include "Common/Vulkan/VulkanLoader.h"
|
||||
#include "Common/Vulkan/VulkanImage.h"
|
||||
|
||||
@ -25,6 +29,24 @@
|
||||
// VulkanFBO is an approximation of the FBO concept the other backends use
|
||||
// to make things as similar as possible without being suboptimal.
|
||||
//
|
||||
// An FBO can be rendered to and used as a texture multiple times in a frame.
|
||||
// Even at multiple sizes, while keeping the same contents.
|
||||
// With GL or D3D we'd just rely on the driver managing duplicates for us, but in
|
||||
// Vulkan we will want to be able to batch up the whole frame and reorder passes
|
||||
// so that all textures are ready before the main scene, instead of switching back and
|
||||
// forth. This comes at a memory cost but will be worth it.
|
||||
//
|
||||
// When we render to a scene, then render to a texture, then go back to the scene and
|
||||
// use that texture, we will register that as a dependency. Then we will walk the DAG
|
||||
// to find the final order of command buffers, and execute it.
|
||||
//
|
||||
// Each FBO will get its own command buffer for each pass.
|
||||
|
||||
//
|
||||
struct VulkanFBOPass {
|
||||
VkCommandBuffer cmd;
|
||||
};
|
||||
|
||||
class VulkanFBO {
|
||||
public:
|
||||
VulkanFBO();
|
||||
@ -45,4 +67,68 @@ private:
|
||||
|
||||
// This point specifically to color and depth.
|
||||
VkFramebuffer framebuffer_;
|
||||
};
|
||||
};
|
||||
|
||||
// Similar to a subset of Thin3D, but separate.
|
||||
// This is used for things like postprocessing shaders, depal, etc.
|
||||
// No UBO data is used, only PushConstants.
|
||||
// No transform matrices, only post-proj coordinates.
|
||||
// Two textures can be sampled.
|
||||
class Vulkan2D {
|
||||
public:
|
||||
Vulkan2D(VulkanContext *vulkan);
|
||||
~Vulkan2D();
|
||||
|
||||
VkPipeline GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShaderModule vs, VkShaderModule fs);
|
||||
|
||||
void BeginFrame();
|
||||
void EndFrame();
|
||||
|
||||
VkDescriptorSet GetDescriptorSet(VkImageView tex1, VkSampler sampler1, VkImageView tex2, VkSampler sampler2);
|
||||
|
||||
// Simple way
|
||||
void BindDescriptorSet(VkCommandBuffer cmd, VkImageView tex1, VkSampler sampler1);
|
||||
|
||||
struct Vertex {
|
||||
float x, y, z;
|
||||
float u, v;
|
||||
};
|
||||
|
||||
private:
|
||||
VulkanContext *vulkan_;
|
||||
VkDescriptorSetLayout descriptorSetLayout_;
|
||||
VkPipelineLayout pipelineLayout_;
|
||||
|
||||
// Yes, another one...
|
||||
struct DescriptorSetKey {
|
||||
VkImageView imageView[2];
|
||||
VkSampler sampler[2];
|
||||
|
||||
bool operator < (const DescriptorSetKey &other) const {
|
||||
return std::tie(imageView[0], imageView[1], sampler[0], sampler[1]) <
|
||||
std::tie(other.imageView[0], other.imageView[1], other.sampler[0], other.sampler[1]);
|
||||
}
|
||||
};
|
||||
|
||||
struct PipelineKey {
|
||||
VkShaderModule vs;
|
||||
VkShaderModule fs;
|
||||
VkRenderPass rp;
|
||||
bool operator < (const PipelineKey &other) const {
|
||||
return std::tie(vs, fs, rp) < std::tie(other.vs, other.fs, other.rp);
|
||||
}
|
||||
};
|
||||
|
||||
struct FrameData {
|
||||
VkDescriptorPool descPool;
|
||||
std::map<DescriptorSetKey, VkDescriptorSet> descSets;
|
||||
};
|
||||
|
||||
FrameData frameData_[2];
|
||||
int curFrame_;
|
||||
|
||||
std::map<PipelineKey, VkPipeline> pipelines_;
|
||||
};
|
||||
|
||||
|
||||
VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits stage, const char *code, std::string *error);
|
@ -128,6 +128,9 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep
|
||||
// https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/121
|
||||
if (msgCode == 6 && (!memcmp(pMsg, "Cannot map", 10) || !memcmp(pMsg, "Cannot sub", 10)))
|
||||
return false;
|
||||
// And for dynamic offsets.
|
||||
if (msgCode == 62 && (!memcmp(pMsg, "VkDesc", 6)))
|
||||
return false;
|
||||
|
||||
#ifdef _WIN32
|
||||
OutputDebugStringA(message.str().c_str());
|
||||
|
Loading…
x
Reference in New Issue
Block a user