Merge pull request #8680 from hrydgard/vulkan-framebuffer

Vulkan framebuffer - First steps
This commit is contained in:
Henrik Rydgård 2016-03-31 09:27:42 +02:00
commit 00d60d0068
14 changed files with 879 additions and 329 deletions

View File

@ -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",

View File

@ -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_;

View File

@ -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");

View File

@ -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;

View File

@ -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);
}
}

View File

@ -143,6 +143,10 @@ public:
void DirtyAllUBOs();
VulkanPushBuffer *GetPushBufferForTextureData() {
return frame_[curFrame_].pushUBO;
}
private:
struct FrameData;

View File

@ -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(&copyInfo, 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);
}

View File

@ -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_;
};

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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());