vulkan: OIT renderer WIP
This commit is contained in:
parent
b8c1d6657d
commit
c10f075266
|
@ -0,0 +1,726 @@
|
|||
/*
|
||||
Created on: Nov 7, 2019
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <math.h>
|
||||
#include "oit_drawer.h"
|
||||
#include "hw/pvr/pvr_mem.h"
|
||||
|
||||
const vk::DeviceSize PixelBufferSize = 512 * 1024 * 1024;
|
||||
|
||||
// FIXME Code dup
|
||||
TileClipping OITDrawer::SetTileClip(u32 val, vk::Rect2D& clipRect)
|
||||
{
|
||||
if (!settings.rend.Clipping)
|
||||
return TileClipping::Off;
|
||||
|
||||
u32 clipmode = val >> 28;
|
||||
if (clipmode < 2)
|
||||
return TileClipping::Off; //always passes
|
||||
|
||||
TileClipping tileClippingMode;
|
||||
if (clipmode & 1)
|
||||
tileClippingMode = TileClipping::Inside; //render stuff outside the region
|
||||
else
|
||||
tileClippingMode = TileClipping::Outside; //render stuff inside the region
|
||||
|
||||
float csx = (float)(val & 63);
|
||||
float cex = (float)((val >> 6) & 63);
|
||||
float csy = (float)((val >> 12) & 31);
|
||||
float cey = (float)((val >> 17) & 31);
|
||||
csx = csx * 32;
|
||||
cex = cex * 32 + 32;
|
||||
csy = csy * 32;
|
||||
cey = cey * 32 + 32;
|
||||
|
||||
if (csx <= 0 && csy <= 0 && cex >= 640 && cey >= 480)
|
||||
return TileClipping::Off;
|
||||
|
||||
if (!pvrrc.isRTT)
|
||||
{
|
||||
glm::vec4 clip_start(csx, csy, 0, 1);
|
||||
glm::vec4 clip_end(cex, cey, 0, 1);
|
||||
clip_start = matrices.GetViewportMatrix() * clip_start;
|
||||
clip_end = matrices.GetViewportMatrix() * clip_end;
|
||||
|
||||
csx = clip_start[0];
|
||||
csy = clip_start[1];
|
||||
cey = clip_end[1];
|
||||
cex = clip_end[0];
|
||||
}
|
||||
else if (!settings.rend.RenderToTextureBuffer)
|
||||
{
|
||||
csx *= settings.rend.RenderToTextureUpscale;
|
||||
csy *= settings.rend.RenderToTextureUpscale;
|
||||
cex *= settings.rend.RenderToTextureUpscale;
|
||||
cey *= settings.rend.RenderToTextureUpscale;
|
||||
}
|
||||
clipRect = vk::Rect2D(vk::Offset2D(std::max(0, (int)lroundf(csx)), std::max(0, (int)lroundf(csy))),
|
||||
vk::Extent2D(std::max(0, (int)lroundf(cex - csx)), std::max(0, (int)lroundf(cey - csy))));
|
||||
|
||||
return tileClippingMode;
|
||||
}
|
||||
|
||||
void OITDrawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool autosort, int pass,
|
||||
const PolyParam& poly, u32 first, u32 count)
|
||||
{
|
||||
vk::Rect2D scissorRect;
|
||||
TileClipping tileClip = SetTileClip(poly.tileclip, scissorRect);
|
||||
if (tileClip != TileClipping::Outside)
|
||||
scissorRect = baseScissor;
|
||||
SetScissor(cmdBuffer, scissorRect);
|
||||
|
||||
float trilinearAlpha = 1.f;
|
||||
if (poly.tsp.FilterMode > 1 && poly.pcw.Texture && listType != ListType_Punch_Through)
|
||||
{
|
||||
trilinearAlpha = 0.25 * (poly.tsp.MipMapD & 0x3);
|
||||
if (poly.tsp.FilterMode == 2)
|
||||
// Trilinear pass A
|
||||
trilinearAlpha = 1.0 - trilinearAlpha;
|
||||
}
|
||||
|
||||
bool twoVolumes = poly.tsp1.full != -1 || poly.tcw1.full != -1;
|
||||
|
||||
OITDescriptorSets::PushConstants pushConstants = {
|
||||
{ (float)scissorRect.offset.x, (float)scissorRect.offset.y,
|
||||
(float)scissorRect.extent.width, (float)scissorRect.extent.height },
|
||||
{ getBlendFactor(poly.tsp.SrcInstr, true), getBlendFactor(poly.tsp.DstInstr, false), 0, 0 },
|
||||
trilinearAlpha,
|
||||
(int)(&poly - (listType == ListType_Opaque ? pvrrc.global_param_op.head()
|
||||
: listType == ListType_Punch_Through ? pvrrc.global_param_pt.head()
|
||||
: pvrrc.global_param_tr.head())),
|
||||
};
|
||||
if (twoVolumes)
|
||||
{
|
||||
pushConstants.blend_mode1 = { getBlendFactor(poly.tsp1.SrcInstr, true), getBlendFactor(poly.tsp1.DstInstr, false), 0, 0 };
|
||||
pushConstants.shading_instr0 = poly.tsp.ShadInstr;
|
||||
pushConstants.shading_instr1 = poly.tsp1.ShadInstr;
|
||||
pushConstants.fog_control0 = poly.tsp.FogCtrl;
|
||||
pushConstants.fog_control1 = poly.tsp1.FogCtrl;
|
||||
pushConstants.use_alpha0 = poly.tsp.UseAlpha;
|
||||
pushConstants.use_alpha1 = poly.tsp1.UseAlpha;
|
||||
pushConstants.ignore_tex_alpha0 = poly.tsp.IgnoreTexA;
|
||||
pushConstants.ignore_tex_alpha1 = poly.tsp1.IgnoreTexA;
|
||||
}
|
||||
cmdBuffer.pushConstants<OITDescriptorSets::PushConstants>(pipelineManager->GetPipelineLayout(), vk::ShaderStageFlagBits::eFragment, 0, pushConstants);
|
||||
|
||||
if (poly.pcw.Texture)
|
||||
GetCurrentDescSet().SetTexture(poly.texid, poly.tsp, poly.texid1, poly.tsp1);
|
||||
|
||||
vk::Pipeline pipeline = pipelineManager->GetPipeline(listType, autosort, poly, pass);
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
if (poly.pcw.Texture)
|
||||
GetCurrentDescSet().BindPerPolyDescriptorSets(cmdBuffer, poly.texid, poly.tsp, poly.texid1, poly.tsp1);
|
||||
|
||||
cmdBuffer.drawIndexed(count, 1, first, 0, 0);
|
||||
}
|
||||
|
||||
void OITDrawer::DrawList(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sortTriangles, int pass,
|
||||
const List<PolyParam>& polys, u32 first, u32 last)
|
||||
{
|
||||
for (u32 i = first; i < last; i++)
|
||||
{
|
||||
const PolyParam &pp = polys.head()[i];
|
||||
DrawPoly(cmdBuffer, listType, sortTriangles, pass, pp, pp.first, pp.count);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Translucent>
|
||||
void OITDrawer::DrawModifierVolumes(const vk::CommandBuffer& cmdBuffer, int first, int count)
|
||||
{
|
||||
if (count == 0 || pvrrc.modtrig.used() == 0)
|
||||
return;
|
||||
|
||||
vk::Buffer buffer = GetMainBuffer(0)->buffer.get();
|
||||
cmdBuffer.bindVertexBuffers(0, 1, &buffer, &offsets.modVolOffset);
|
||||
|
||||
ModifierVolumeParam* params = Translucent ? &pvrrc.global_param_mvo_tr.head()[first] : &pvrrc.global_param_mvo.head()[first];
|
||||
|
||||
// TODO glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT);
|
||||
|
||||
int mod_base = -1;
|
||||
vk::Pipeline pipeline;
|
||||
|
||||
for (u32 cmv = 0; cmv < count; cmv++)
|
||||
{
|
||||
ModifierVolumeParam& param = params[cmv];
|
||||
|
||||
if (param.count == 0)
|
||||
continue;
|
||||
|
||||
u32 mv_mode = param.isp.DepthMode;
|
||||
|
||||
verify(param.first >= 0 && param.first + param.count <= pvrrc.modtrig.used());
|
||||
|
||||
if (mod_base == -1)
|
||||
mod_base = param.first;
|
||||
|
||||
if (!param.isp.VolumeLast && mv_mode > 0)
|
||||
{
|
||||
// OR'ing (open volume or quad)
|
||||
if (Translucent)
|
||||
pipeline = pipelineManager->GetTrModifierVolumePipeline(ModVolMode::Or);
|
||||
else
|
||||
pipeline = pipelineManager->GetModifierVolumePipeline(ModVolMode::Or);
|
||||
}
|
||||
else
|
||||
{
|
||||
// XOR'ing (closed volume)
|
||||
if (Translucent)
|
||||
pipeline = pipelineManager->GetTrModifierVolumePipeline(ModVolMode::Xor);
|
||||
else
|
||||
pipeline = pipelineManager->GetModifierVolumePipeline(ModVolMode::Xor);
|
||||
}
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
cmdBuffer.draw(param.count * 3, 1, param.first * 3, 0);
|
||||
|
||||
// TODO glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
|
||||
|
||||
if (mv_mode == 1 || mv_mode == 2)
|
||||
{
|
||||
//Sum the area
|
||||
if (Translucent)
|
||||
pipeline = pipelineManager->GetTrModifierVolumePipeline(mv_mode == 1 ? ModVolMode::Inclusion : ModVolMode::Exclusion);
|
||||
else
|
||||
pipeline = pipelineManager->GetModifierVolumePipeline(mv_mode == 1 ? ModVolMode::Inclusion : ModVolMode::Exclusion);
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
cmdBuffer.draw((param.first + param.count - mod_base) * 3, 1, mod_base * 3, 0);
|
||||
|
||||
// TODO glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
|
||||
mod_base = -1;
|
||||
}
|
||||
}
|
||||
const vk::DeviceSize offset = 0;
|
||||
cmdBuffer.bindVertexBuffers(0, 1, &buffer, &offset);
|
||||
}
|
||||
|
||||
void OITDrawer::UploadMainBuffer(const OITDescriptorSets::VertexShaderUniforms& vertexUniforms,
|
||||
const OITDescriptorSets::FragmentShaderUniforms& fragmentUniforms)
|
||||
{
|
||||
using VertexShaderUniforms = OITDescriptorSets::VertexShaderUniforms;
|
||||
using FragmentShaderUniforms = OITDescriptorSets::FragmentShaderUniforms;
|
||||
|
||||
// TODO Put this logic in an allocator
|
||||
std::vector<const void *> chunks;
|
||||
std::vector<u32> chunkSizes;
|
||||
|
||||
// Vertex
|
||||
chunks.push_back(pvrrc.verts.head());
|
||||
chunkSizes.push_back(pvrrc.verts.bytes());
|
||||
|
||||
u32 padding = align(pvrrc.verts.bytes(), 4);
|
||||
offsets.modVolOffset = pvrrc.verts.bytes() + padding;
|
||||
chunks.push_back(nullptr);
|
||||
chunkSizes.push_back(padding);
|
||||
|
||||
// Modifier Volumes
|
||||
chunks.push_back(pvrrc.modtrig.head());
|
||||
chunkSizes.push_back(pvrrc.modtrig.bytes());
|
||||
padding = align(offsets.modVolOffset + pvrrc.modtrig.bytes(), 4);
|
||||
offsets.indexOffset = offsets.modVolOffset + pvrrc.modtrig.bytes() + padding;
|
||||
chunks.push_back(nullptr);
|
||||
chunkSizes.push_back(padding);
|
||||
|
||||
// Index
|
||||
chunks.push_back(pvrrc.idx.head());
|
||||
chunkSizes.push_back(pvrrc.idx.bytes());
|
||||
|
||||
// Uniform buffers
|
||||
u32 indexSize = pvrrc.idx.bytes();
|
||||
padding = align(offsets.indexOffset + indexSize, std::max(4, (int)GetContext()->GetUniformBufferAlignment()));
|
||||
offsets.vertexUniformOffset = offsets.indexOffset + indexSize + padding;
|
||||
chunks.push_back(nullptr);
|
||||
chunkSizes.push_back(padding);
|
||||
|
||||
chunks.push_back(&vertexUniforms);
|
||||
chunkSizes.push_back(sizeof(vertexUniforms));
|
||||
padding = align(offsets.vertexUniformOffset + sizeof(VertexShaderUniforms), std::max(4, (int)GetContext()->GetUniformBufferAlignment()));
|
||||
offsets.fragmentUniformOffset = offsets.vertexUniformOffset + sizeof(VertexShaderUniforms) + padding;
|
||||
chunks.push_back(nullptr);
|
||||
chunkSizes.push_back(padding);
|
||||
|
||||
chunks.push_back(&fragmentUniforms);
|
||||
chunkSizes.push_back(sizeof(fragmentUniforms));
|
||||
|
||||
// Translucent poly params
|
||||
padding = align(offsets.fragmentUniformOffset + sizeof(FragmentShaderUniforms), std::max(4, (int)GetContext()->GetStorageBufferAlignment()));
|
||||
offsets.polyParamsOffset = offsets.fragmentUniformOffset + sizeof(FragmentShaderUniforms) + padding;
|
||||
chunks.push_back(nullptr);
|
||||
chunkSizes.push_back(padding);
|
||||
|
||||
chunks.push_back(pvrrc.global_param_tr.head());
|
||||
chunkSizes.push_back(pvrrc.global_param_tr.bytes());
|
||||
u32 totalSize = offsets.polyParamsOffset + pvrrc.global_param_tr.bytes();
|
||||
|
||||
BufferData *buffer = GetMainBuffer(totalSize);
|
||||
buffer->upload(chunks.size(), &chunkSizes[0], &chunks[0]);
|
||||
}
|
||||
|
||||
bool OITDrawer::Draw(const Texture *fogTexture)
|
||||
{
|
||||
extern bool fog_needs_update;
|
||||
|
||||
OITDescriptorSets::VertexShaderUniforms vtxUniforms;
|
||||
vtxUniforms.normal_matrix = matrices.GetNormalMatrix();
|
||||
|
||||
OITDescriptorSets::FragmentShaderUniforms fragUniforms;
|
||||
fragUniforms.extra_depth_scale = settings.rend.ExtraDepthScale;
|
||||
|
||||
//VERT and RAM fog color constants
|
||||
u8* fog_colvert_bgra=(u8*)&FOG_COL_VERT;
|
||||
u8* fog_colram_bgra=(u8*)&FOG_COL_RAM;
|
||||
fragUniforms.sp_FOG_COL_VERT[0]=fog_colvert_bgra[2]/255.0f;
|
||||
fragUniforms.sp_FOG_COL_VERT[1]=fog_colvert_bgra[1]/255.0f;
|
||||
fragUniforms.sp_FOG_COL_VERT[2]=fog_colvert_bgra[0]/255.0f;
|
||||
|
||||
fragUniforms.sp_FOG_COL_RAM[0]=fog_colram_bgra [2]/255.0f;
|
||||
fragUniforms.sp_FOG_COL_RAM[1]=fog_colram_bgra [1]/255.0f;
|
||||
fragUniforms.sp_FOG_COL_RAM[2]=fog_colram_bgra [0]/255.0f;
|
||||
|
||||
//Fog density constant
|
||||
u8* fog_density=(u8*)&FOG_DENSITY;
|
||||
float fog_den_mant=fog_density[1]/128.0f; //bit 7 -> x. bit, so [6:0] -> fraction -> /128
|
||||
s32 fog_den_exp=(s8)fog_density[0];
|
||||
fragUniforms.sp_FOG_DENSITY = fog_den_mant * powf(2.0f, fog_den_exp);
|
||||
|
||||
fragUniforms.colorClampMin[0] = ((pvrrc.fog_clamp_min >> 16) & 0xFF) / 255.0f;
|
||||
fragUniforms.colorClampMin[1] = ((pvrrc.fog_clamp_min >> 8) & 0xFF) / 255.0f;
|
||||
fragUniforms.colorClampMin[2] = ((pvrrc.fog_clamp_min >> 0) & 0xFF) / 255.0f;
|
||||
fragUniforms.colorClampMin[3] = ((pvrrc.fog_clamp_min >> 24) & 0xFF) / 255.0f;
|
||||
|
||||
fragUniforms.colorClampMax[0] = ((pvrrc.fog_clamp_max >> 16) & 0xFF) / 255.0f;
|
||||
fragUniforms.colorClampMax[1] = ((pvrrc.fog_clamp_max >> 8) & 0xFF) / 255.0f;
|
||||
fragUniforms.colorClampMax[2] = ((pvrrc.fog_clamp_max >> 0) & 0xFF) / 255.0f;
|
||||
fragUniforms.colorClampMax[3] = ((pvrrc.fog_clamp_max >> 24) & 0xFF) / 255.0f;
|
||||
|
||||
fragUniforms.cp_AlphaTestValue = (PT_ALPHA_REF & 0xFF) / 255.0f;
|
||||
fragUniforms.shade_scale_factor = FPU_SHAD_SCALE.scale_factor / 256.f;
|
||||
|
||||
currentScissor = vk::Rect2D();
|
||||
|
||||
vk::CommandBuffer cmdBuffer = BeginRenderPass();
|
||||
|
||||
// Upload vertex and index buffers
|
||||
UploadMainBuffer(vtxUniforms, fragUniforms);
|
||||
|
||||
quadBuffer->Update();
|
||||
|
||||
// Update per-frame descriptor set and bind it
|
||||
GetCurrentDescSet().UpdateUniforms(GetMainBuffer(0)->buffer.get(), offsets.vertexUniformOffset, offsets.fragmentUniformOffset,
|
||||
fogTexture->GetImageView(), pixelBuffer->buffer.get(), PixelBufferSize, pixelCounter->buffer.get(), offsets.polyParamsOffset,
|
||||
pvrrc.global_param_tr.bytes(), abufferPointerAttachment->GetImageView());
|
||||
GetCurrentDescSet().BindPerFrameDescriptorSets(cmdBuffer);
|
||||
// Reset per-poly descriptor set pool
|
||||
GetCurrentDescSet().Reset();
|
||||
|
||||
RenderPass previous_pass = {};
|
||||
for (int render_pass = 0; render_pass < pvrrc.render_passes.used(); render_pass++)
|
||||
{
|
||||
const RenderPass& current_pass = pvrrc.render_passes.head()[render_pass];
|
||||
|
||||
DEBUG_LOG(RENDERER, "Render pass %d OP %d PT %d TR %d MV %d autosort %d", render_pass + 1,
|
||||
current_pass.op_count - previous_pass.op_count,
|
||||
current_pass.pt_count - previous_pass.pt_count,
|
||||
current_pass.tr_count - previous_pass.tr_count,
|
||||
current_pass.mvo_count - previous_pass.mvo_count, current_pass.autosort);
|
||||
// Bind vertex and index buffers
|
||||
const vk::DeviceSize zeroOffset[] = { 0 };
|
||||
const vk::Buffer buffer = GetMainBuffer(0)->buffer.get();
|
||||
cmdBuffer.bindVertexBuffers(0, 1, &buffer, zeroOffset);
|
||||
cmdBuffer.bindIndexBuffer(buffer, offsets.indexOffset, vk::IndexType::eUint32);
|
||||
|
||||
// Depth + stencil subpass
|
||||
DrawList(cmdBuffer, ListType_Opaque, false, 0, pvrrc.global_param_op, previous_pass.op_count, current_pass.op_count);
|
||||
DrawList(cmdBuffer, ListType_Punch_Through, false, 0, pvrrc.global_param_pt, previous_pass.pt_count, current_pass.pt_count);
|
||||
|
||||
DrawModifierVolumes<false>(cmdBuffer, previous_pass.mvo_count, current_pass.mvo_count - previous_pass.mvo_count);
|
||||
|
||||
// Color subpass
|
||||
cmdBuffer.nextSubpass(vk::SubpassContents::eInline);
|
||||
GetCurrentDescSet().UpdatePass1Uniforms(stencilImageView, depthImageView);
|
||||
GetCurrentDescSet().BindPass1DescriptorSets(cmdBuffer);
|
||||
|
||||
// OP + PT
|
||||
DrawList(cmdBuffer, ListType_Opaque, false, 1, pvrrc.global_param_op, previous_pass.op_count, current_pass.op_count);
|
||||
DrawList(cmdBuffer, ListType_Punch_Through, false, 1, pvrrc.global_param_pt, previous_pass.pt_count, current_pass.pt_count);
|
||||
|
||||
// TR
|
||||
DrawList(cmdBuffer, ListType_Translucent, current_pass.autosort, 3, pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count);
|
||||
|
||||
// Final subpass
|
||||
cmdBuffer.nextSubpass(vk::SubpassContents::eInline);
|
||||
GetCurrentDescSet().UpdatePass2Uniforms(colorImageView);
|
||||
GetCurrentDescSet().BindPass2DescriptorSets(cmdBuffer);
|
||||
|
||||
// Tr modifier volumes
|
||||
DrawModifierVolumes<true>(cmdBuffer, previous_pass.mvo_tr_count, current_pass.mvo_tr_count - previous_pass.mvo_tr_count);
|
||||
|
||||
vk::Pipeline pipeline = pipelineManager->GetFinalPipeline(current_pass.autosort);
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
quadBuffer->Bind(cmdBuffer);
|
||||
quadBuffer->Draw(cmdBuffer);
|
||||
|
||||
// Clear
|
||||
/*
|
||||
vk::ImageMemoryBarrier imageMemoryBarrier(
|
||||
vk::AccessFlagBits::eShaderRead,
|
||||
vk::AccessFlagBits::eShaderWrite,
|
||||
vk::ImageLayout::eGeneral,
|
||||
vk::ImageLayout::eGeneral,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
abufferPointerAttachment->GetImage(),
|
||||
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor));
|
||||
cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||
*/
|
||||
vk::MemoryBarrier memoryBarrier(vk::AccessFlagBits::eShaderRead, vk::AccessFlagBits::eShaderWrite);
|
||||
cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::DependencyFlagBits::eByRegion, 1, &memoryBarrier, 0, nullptr, 0, nullptr);
|
||||
pipeline = pipelineManager->GetClearPipeline();
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
quadBuffer->Draw(cmdBuffer);
|
||||
|
||||
previous_pass = current_pass;
|
||||
}
|
||||
|
||||
return !pvrrc.isRTT;
|
||||
}
|
||||
|
||||
void OITDrawer::MakeBuffers(int width, int height)
|
||||
{
|
||||
pixelBuffer = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(), PixelBufferSize,
|
||||
vk::BufferUsageFlagBits::eStorageBuffer, &SimpleAllocator::instance, vk::MemoryPropertyFlagBits::eDeviceLocal));
|
||||
pixelCounter = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(), 4,
|
||||
vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst, allocator, vk::MemoryPropertyFlagBits::eDeviceLocal));
|
||||
pixelCounterReset = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(), 4,
|
||||
vk::BufferUsageFlagBits::eTransferSrc, allocator));
|
||||
const int zero = 0;
|
||||
pixelCounterReset->upload(sizeof(zero), &zero);
|
||||
abufferPointerAttachment = std::unique_ptr<FramebufferAttachment>(new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(),
|
||||
allocator));
|
||||
abufferPointerAttachment->Init(width, height, vk::Format::eR32Uint, vk::ImageUsageFlagBits::eStorage);
|
||||
abufferPointerTransitionNeeded = true;
|
||||
}
|
||||
|
||||
void OITDrawer::MakeAttachments(int width, int height)
|
||||
{
|
||||
colorAttachment = std::unique_ptr<FramebufferAttachment>(new FramebufferAttachment(GetContext()->GetPhysicalDevice(),
|
||||
GetContext()->GetDevice(), allocator));
|
||||
colorAttachment->Init(width, height, GetContext()->GetColorFormat(), vk::ImageUsageFlagBits::eInputAttachment);
|
||||
colorImageView = colorAttachment->GetImageView();
|
||||
|
||||
depthAttachment = std::unique_ptr<FramebufferAttachment>(new FramebufferAttachment(GetContext()->GetPhysicalDevice(),
|
||||
GetContext()->GetDevice(), allocator));
|
||||
depthAttachment->Init(width, height, GetContext()->GetDepthFormat(), vk::ImageUsageFlagBits::eInputAttachment);
|
||||
depthImageView = depthAttachment->GetImageView();
|
||||
stencilImageView = depthAttachment->GetStencilView();
|
||||
|
||||
depth2Attachment = std::unique_ptr<FramebufferAttachment>(new FramebufferAttachment(GetContext()->GetPhysicalDevice(),
|
||||
GetContext()->GetDevice(), allocator));
|
||||
depth2Attachment->Init(width, height, GetContext()->GetDepthFormat());
|
||||
printf("color attachment %p depth %p depth2 %p\n", (VkImage)colorAttachment->GetImage(), (VkImage)depthAttachment->GetImage(),
|
||||
(VkImage)depth2Attachment->GetImage());
|
||||
}
|
||||
|
||||
void OITDrawer::MakeFramebuffers(int width, int height)
|
||||
{
|
||||
vk::ImageView attachments[] = {
|
||||
nullptr, // swap chain image view, set later
|
||||
colorImageView,
|
||||
depthImageView,
|
||||
depth2Attachment->GetImageView()
|
||||
};
|
||||
framebuffers.reserve(GetContext()->GetSwapChainSize());
|
||||
for (int i = 0; i < GetContext()->GetSwapChainSize(); i++)
|
||||
{
|
||||
vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), pipelineManager->GetRenderPass(),
|
||||
ARRAY_SIZE(attachments), attachments, width, height, 1);
|
||||
attachments[0] = GetContext()->GetSwapChainImageView(i);
|
||||
framebuffers.push_back(GetContext()->GetDevice().createFramebufferUnique(createInfo));
|
||||
}
|
||||
}
|
||||
|
||||
vk::CommandBuffer OITTextureDrawer::BeginRenderPass()
|
||||
{
|
||||
DEBUG_LOG(RENDERER, "RenderToTexture packmode=%d stride=%d - %d,%d -> %d,%d", FB_W_CTRL.fb_packmode, FB_W_LINESTRIDE.stride * 8,
|
||||
FB_X_CLIP.min, FB_Y_CLIP.min, FB_X_CLIP.max, FB_Y_CLIP.max);
|
||||
matrices.CalcMatrices(&pvrrc);
|
||||
|
||||
textureAddr = FB_W_SOF1 & VRAM_MASK;
|
||||
u32 origWidth = pvrrc.fb_X_CLIP.max - pvrrc.fb_X_CLIP.min + 1;
|
||||
u32 origHeight = pvrrc.fb_Y_CLIP.max - pvrrc.fb_Y_CLIP.min + 1;
|
||||
u32 upscaledWidth = origWidth;
|
||||
u32 upscaledHeight = origHeight;
|
||||
int heightPow2 = 2;
|
||||
while (heightPow2 < upscaledHeight)
|
||||
heightPow2 *= 2;
|
||||
int widthPow2 = 2;
|
||||
while (widthPow2 < upscaledWidth)
|
||||
widthPow2 *= 2;
|
||||
|
||||
if (settings.rend.RenderToTextureUpscale > 1 && !settings.rend.RenderToTextureBuffer)
|
||||
{
|
||||
upscaledWidth *= settings.rend.RenderToTextureUpscale;
|
||||
upscaledHeight *= settings.rend.RenderToTextureUpscale;
|
||||
widthPow2 *= settings.rend.RenderToTextureUpscale;
|
||||
heightPow2 *= settings.rend.RenderToTextureUpscale;
|
||||
}
|
||||
|
||||
static_cast<RttOITPipelineManager*>(pipelineManager)->CheckSettingsChange();
|
||||
VulkanContext *context = GetContext();
|
||||
vk::Device device = context->GetDevice();
|
||||
|
||||
vk::CommandBuffer commandBuffer = commandPool->Allocate();
|
||||
commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
|
||||
|
||||
if (widthPow2 != this->width || heightPow2 != this->height || !depthAttachment)
|
||||
{
|
||||
if (!depthAttachment)
|
||||
depthAttachment = std::unique_ptr<FramebufferAttachment>(new FramebufferAttachment(context->GetPhysicalDevice(), device, allocator));
|
||||
depthAttachment->Init(widthPow2, heightPow2, GetContext()->GetDepthFormat());
|
||||
}
|
||||
vk::ImageView colorImageView;
|
||||
vk::ImageLayout colorImageCurrentLayout;
|
||||
|
||||
if (!settings.rend.RenderToTextureBuffer)
|
||||
{
|
||||
// TexAddr : fb_rtt.TexAddr, Reserved : 0, StrideSel : 0, ScanOrder : 1
|
||||
TCW tcw = { { textureAddr >> 3, 0, 0, 1 } };
|
||||
switch (FB_W_CTRL.fb_packmode) {
|
||||
case 0:
|
||||
case 3:
|
||||
tcw.PixelFmt = Pixel1555;
|
||||
break;
|
||||
case 1:
|
||||
tcw.PixelFmt = Pixel565;
|
||||
break;
|
||||
case 2:
|
||||
tcw.PixelFmt = Pixel4444;
|
||||
break;
|
||||
}
|
||||
|
||||
TSP tsp = { 0 };
|
||||
for (tsp.TexU = 0; tsp.TexU <= 7 && (8 << tsp.TexU) < origWidth; tsp.TexU++);
|
||||
for (tsp.TexV = 0; tsp.TexV <= 7 && (8 << tsp.TexV) < origHeight; tsp.TexV++);
|
||||
|
||||
texture = textureCache->getTextureCacheData(tsp, tcw);
|
||||
if (texture->IsNew())
|
||||
{
|
||||
texture->Create();
|
||||
texture->SetAllocator(allocator);
|
||||
texture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
|
||||
texture->SetDevice(device);
|
||||
}
|
||||
if (texture->format != vk::Format::eR8G8B8A8Unorm)
|
||||
{
|
||||
texture->extent = vk::Extent2D(widthPow2, heightPow2);
|
||||
texture->format = vk::Format::eR8G8B8A8Unorm;
|
||||
texture->CreateImage(vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled,
|
||||
vk::ImageLayout::eUndefined, vk::MemoryPropertyFlags(), vk::ImageAspectFlagBits::eColor);
|
||||
colorImageCurrentLayout = vk::ImageLayout::eUndefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
colorImageCurrentLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||
}
|
||||
colorImage = *texture->image;
|
||||
colorImageView = texture->GetImageView();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (widthPow2 != this->width || heightPow2 != this->height || !colorAttachment)
|
||||
{
|
||||
if (!colorAttachment)
|
||||
{
|
||||
colorAttachment = std::unique_ptr<FramebufferAttachment>(new FramebufferAttachment(context->GetPhysicalDevice(),
|
||||
device, allocator));
|
||||
}
|
||||
colorAttachment->Init(widthPow2, heightPow2, vk::Format::eR8G8B8A8Unorm);
|
||||
}
|
||||
colorImage = colorAttachment->GetImage();
|
||||
colorImageView = colorAttachment->GetImageView();
|
||||
colorImageCurrentLayout = vk::ImageLayout::eUndefined;
|
||||
}
|
||||
width = widthPow2;
|
||||
height = heightPow2;
|
||||
|
||||
setImageLayout(commandBuffer, colorImage, vk::Format::eR8G8B8A8Unorm, 1, colorImageCurrentLayout, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
|
||||
vk::ImageView imageViews[] = {
|
||||
colorImageView,
|
||||
depthAttachment->GetImageView(),
|
||||
};
|
||||
framebuffer = device.createFramebufferUnique(vk::FramebufferCreateInfo(vk::FramebufferCreateFlags(),
|
||||
pipelineManager->GetRenderPass(), ARRAY_SIZE(imageViews), imageViews, widthPow2, heightPow2, 1));
|
||||
|
||||
const vk::ClearValue clear_colors[] = { vk::ClearColorValue(std::array<float, 4> { 0.f, 0.f, 0.f, 1.f }), vk::ClearDepthStencilValue { 0.f, 0 } };
|
||||
commandBuffer.beginRenderPass(vk::RenderPassBeginInfo(pipelineManager->GetRenderPass(), *framebuffer,
|
||||
vk::Rect2D( { 0, 0 }, { width, height }), 2, clear_colors), vk::SubpassContents::eInline);
|
||||
commandBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, (float)upscaledWidth, (float)upscaledHeight, 1.0f, 0.0f));
|
||||
baseScissor = vk::Rect2D(vk::Offset2D(0, 0), vk::Extent2D(upscaledWidth, upscaledHeight));
|
||||
commandBuffer.setScissor(0, baseScissor);
|
||||
currentCommandBuffer = commandBuffer;
|
||||
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
void OITTextureDrawer::EndRenderPass()
|
||||
{
|
||||
currentCommandBuffer.endRenderPass();
|
||||
|
||||
if (settings.rend.RenderToTextureBuffer)
|
||||
{
|
||||
vk::BufferImageCopy copyRegion(0, width, height, vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), vk::Offset3D(0, 0, 0),
|
||||
vk::Extent3D(vk::Extent2D(width, height), 1));
|
||||
currentCommandBuffer.copyImageToBuffer(colorAttachment->GetImage(), vk::ImageLayout::eTransferSrcOptimal,
|
||||
*colorAttachment->GetBufferData()->buffer, copyRegion);
|
||||
|
||||
vk::BufferMemoryBarrier bufferMemoryBarrier(
|
||||
vk::AccessFlagBits::eTransferWrite,
|
||||
vk::AccessFlagBits::eHostRead,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
*colorAttachment->GetBufferData()->buffer,
|
||||
0,
|
||||
VK_WHOLE_SIZE);
|
||||
currentCommandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::PipelineStageFlagBits::eHost, {}, nullptr, bufferMemoryBarrier, nullptr);
|
||||
}
|
||||
currentCommandBuffer.end();
|
||||
|
||||
GetContext()->GetGraphicsQueue().submit(vk::SubmitInfo(0, nullptr, nullptr, 1, ¤tCommandBuffer),
|
||||
settings.rend.RenderToTextureBuffer ? *fence : nullptr);
|
||||
colorImage = nullptr;
|
||||
currentCommandBuffer = nullptr;
|
||||
commandPool->EndFrame();
|
||||
|
||||
|
||||
|
||||
if (settings.rend.RenderToTextureBuffer)
|
||||
{
|
||||
GetContext()->GetDevice().waitForFences(1, &fence.get(), true, UINT64_MAX);
|
||||
GetContext()->GetDevice().resetFences(1, &fence.get());
|
||||
|
||||
u16 *dst = (u16 *)&vram[textureAddr];
|
||||
|
||||
PixelBuffer<u32> tmpBuf;
|
||||
tmpBuf.init(width, height);
|
||||
colorAttachment->GetBufferData()->download(GetContext()->GetDevice(), width * height * 4, tmpBuf.data());
|
||||
WriteTextureToVRam(width, height, (u8 *)tmpBuf.data(), dst);
|
||||
|
||||
return;
|
||||
}
|
||||
//memset(&vram[fb_rtt.TexAddr << 3], '\0', size);
|
||||
|
||||
texture->dirty = 0;
|
||||
if (texture->lock_block == NULL)
|
||||
texture->lock_block = libCore_vramlock_Lock(texture->sa_tex, texture->sa + texture->size - 1, texture);
|
||||
}
|
||||
|
||||
vk::CommandBuffer OITScreenDrawer::BeginRenderPass()
|
||||
{
|
||||
GetContext()->NewFrame();
|
||||
vk::CommandBuffer commandBuffer = GetContext()->GetCurrentCommandBuffer();
|
||||
|
||||
// FIXME this needs to go in the parent class
|
||||
if (abufferPointerTransitionNeeded)
|
||||
{
|
||||
abufferPointerTransitionNeeded = false;
|
||||
|
||||
vk::ImageSubresourceRange imageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1);
|
||||
vk::ImageMemoryBarrier imageMemoryBarrier(vk::AccessFlags(), vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eGeneral, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
|
||||
abufferPointerAttachment->GetImage(), imageSubresourceRange);
|
||||
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eFragmentShader, {}, nullptr, nullptr,
|
||||
imageMemoryBarrier);
|
||||
}
|
||||
// FIXME this must be done for each continuation
|
||||
vk::BufferCopy copy(0, 0, sizeof(int));
|
||||
commandBuffer.copyBuffer(*pixelCounterReset->buffer, *pixelCounter->buffer, 1, ©);
|
||||
|
||||
|
||||
vk::Extent2D viewport = GetContext()->GetViewPort();
|
||||
const vk::ClearValue clear_colors[] = {
|
||||
vk::ClearColorValue(std::array<float, 4>{0.f, 0.f, 0.f, 1.f}),
|
||||
vk::ClearColorValue(std::array<float, 4>{0.f, 0.f, 0.f, 1.f}),
|
||||
vk::ClearDepthStencilValue{ 0.f, 0 },
|
||||
vk::ClearDepthStencilValue{ 0.f, 0 },
|
||||
};
|
||||
commandBuffer.beginRenderPass(
|
||||
vk::RenderPassBeginInfo(pipelineManager->GetRenderPass(), *framebuffers[GetContext()->GetCurrentImageIndex()],
|
||||
vk::Rect2D({0, 0}, {viewport.width, viewport.height}), ARRAY_SIZE(clear_colors), clear_colors),
|
||||
vk::SubpassContents::eInline);
|
||||
|
||||
commandBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, (float)screen_width, (float)screen_height, 1.0f, 0.0f));
|
||||
|
||||
matrices.CalcMatrices(&pvrrc);
|
||||
|
||||
bool wide_screen_on = settings.rend.WideScreen && !pvrrc.isRenderFramebuffer && !matrices.IsClipped();
|
||||
|
||||
if (!wide_screen_on)
|
||||
{
|
||||
float width;
|
||||
float height;
|
||||
float min_x;
|
||||
float min_y;
|
||||
|
||||
if (pvrrc.isRenderFramebuffer)
|
||||
{
|
||||
width = 640;
|
||||
height = 480;
|
||||
min_x = 0;
|
||||
min_y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
glm::vec4 clip_min(pvrrc.fb_X_CLIP.min, pvrrc.fb_Y_CLIP.min, 0, 1);
|
||||
glm::vec4 clip_dim(pvrrc.fb_X_CLIP.max - pvrrc.fb_X_CLIP.min + 1,
|
||||
pvrrc.fb_Y_CLIP.max - pvrrc.fb_Y_CLIP.min + 1, 0, 0);
|
||||
clip_min = matrices.GetScissorMatrix() * clip_min;
|
||||
clip_dim = matrices.GetScissorMatrix() * clip_dim;
|
||||
|
||||
min_x = clip_min[0];
|
||||
min_y = clip_min[1];
|
||||
width = clip_dim[0];
|
||||
height = clip_dim[1];
|
||||
if (width < 0)
|
||||
{
|
||||
min_x += width;
|
||||
width = -width;
|
||||
}
|
||||
if (height < 0)
|
||||
{
|
||||
min_y += height;
|
||||
height = -height;
|
||||
}
|
||||
}
|
||||
|
||||
baseScissor = vk::Rect2D(
|
||||
vk::Offset2D((u32)std::max(lroundf(min_x), 0L), (u32)std::max(lroundf(min_y), 0L)),
|
||||
vk::Extent2D((u32)std::max(lroundf(width), 0L), (u32)std::max(lroundf(height), 0L)));
|
||||
}
|
||||
else
|
||||
{
|
||||
baseScissor = vk::Rect2D(vk::Offset2D(0, 0), vk::Extent2D(screen_width, screen_height));
|
||||
}
|
||||
|
||||
commandBuffer.setScissor(0, baseScissor);
|
||||
return commandBuffer;
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
Created on: Nov 6, 2019
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "rend/sorter.h"
|
||||
#include "rend/transform_matrix.h"
|
||||
#include "vulkan.h"
|
||||
#include "buffer.h"
|
||||
#include "commandpool.h"
|
||||
#include "oit_pipeline.h"
|
||||
#include "oit_shaders.h"
|
||||
#include "texture.h"
|
||||
#include "quad.h"
|
||||
|
||||
class OITDrawer
|
||||
{
|
||||
public:
|
||||
OITDrawer() = default;
|
||||
virtual ~OITDrawer() = default;
|
||||
bool Draw(const Texture *fogTexture);
|
||||
OITDrawer(const OITDrawer& other) = delete;
|
||||
OITDrawer(OITDrawer&& other) = default;
|
||||
OITDrawer& operator=(const OITDrawer& other) = delete;
|
||||
OITDrawer& operator=(OITDrawer&& other) = default;
|
||||
|
||||
virtual vk::CommandBuffer BeginRenderPass() = 0;
|
||||
virtual void EndRenderPass() = 0;
|
||||
void SetScissor(const vk::CommandBuffer& cmdBuffer, vk::Rect2D scissor)
|
||||
{
|
||||
if (scissor != currentScissor)
|
||||
{
|
||||
cmdBuffer.setScissor(0, scissor);
|
||||
currentScissor = scissor;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void Init(SamplerManager *samplerManager, Allocator *allocator, OITPipelineManager *pipelineManager)
|
||||
{
|
||||
this->pipelineManager = pipelineManager;
|
||||
this->allocator = allocator;
|
||||
this->samplerManager = samplerManager;
|
||||
if (!quadBuffer)
|
||||
quadBuffer = std::unique_ptr<QuadBuffer>(new QuadBuffer(allocator));
|
||||
vk::Extent2D viewport = GetContext()->GetViewPort();
|
||||
MakeAttachments(viewport.width, viewport.height);
|
||||
MakeFramebuffers(viewport.width, viewport.height);
|
||||
MakeBuffers(viewport.width, viewport.height);
|
||||
}
|
||||
virtual OITDescriptorSets& GetCurrentDescSet() = 0;
|
||||
virtual BufferData *GetMainBuffer(u32 size) = 0;
|
||||
|
||||
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
|
||||
|
||||
OITPipelineManager *pipelineManager = nullptr;
|
||||
vk::Rect2D baseScissor;
|
||||
TransformMatrix<false> matrices;
|
||||
Allocator *allocator = nullptr;
|
||||
std::vector<vk::UniqueFramebuffer> framebuffers;
|
||||
std::unique_ptr<FramebufferAttachment> abufferPointerAttachment;
|
||||
bool abufferPointerTransitionNeeded = false;
|
||||
|
||||
private:
|
||||
TileClipping SetTileClip(u32 val, vk::Rect2D& clipRect);
|
||||
void DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sortTriangles, int pass,
|
||||
const PolyParam& poly, u32 first, u32 count);
|
||||
void DrawList(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sortTriangles, int pass,
|
||||
const List<PolyParam>& polys, u32 first, u32 count);
|
||||
template<bool Translucent>
|
||||
void DrawModifierVolumes(const vk::CommandBuffer& cmdBuffer, int first, int count);
|
||||
void UploadMainBuffer(const OITDescriptorSets::VertexShaderUniforms& vertexUniforms,
|
||||
const OITDescriptorSets::FragmentShaderUniforms& fragmentUniforms);
|
||||
u32 align(vk::DeviceSize offset, u32 alignment)
|
||||
{
|
||||
return (u32)(alignment - (offset & (alignment - 1)));
|
||||
}
|
||||
void MakeAttachments(int width, int height);
|
||||
void MakeFramebuffers(int width, int height);
|
||||
void MakeBuffers(int width, int height);
|
||||
|
||||
struct {
|
||||
vk::DeviceSize indexOffset = 0;
|
||||
vk::DeviceSize modVolOffset = 0;
|
||||
vk::DeviceSize vertexUniformOffset = 0;
|
||||
vk::DeviceSize fragmentUniformOffset = 0;
|
||||
vk::DeviceSize polyParamsOffset = 0;
|
||||
} offsets;
|
||||
|
||||
std::unique_ptr<QuadBuffer> quadBuffer;
|
||||
|
||||
vk::ImageView colorImageView;
|
||||
vk::ImageView depthImageView;
|
||||
vk::ImageView stencilImageView;
|
||||
|
||||
std::unique_ptr<FramebufferAttachment> colorAttachment;
|
||||
std::unique_ptr<FramebufferAttachment> depthAttachment;
|
||||
std::unique_ptr<FramebufferAttachment> depth2Attachment;
|
||||
|
||||
SamplerManager *samplerManager = nullptr;
|
||||
vk::Rect2D currentScissor;
|
||||
std::unique_ptr<BufferData> pixelBuffer;
|
||||
protected: //FIXME
|
||||
std::unique_ptr<BufferData> pixelCounter;
|
||||
std::unique_ptr<BufferData> pixelCounterReset;
|
||||
};
|
||||
|
||||
class OITScreenDrawer : public OITDrawer
|
||||
{
|
||||
public:
|
||||
void Init(SamplerManager *samplerManager, Allocator *allocator, OITShaderManager *shaderManager)
|
||||
{
|
||||
if (!screenPipelineManager)
|
||||
screenPipelineManager = std::unique_ptr<OITPipelineManager>(new OITPipelineManager());
|
||||
screenPipelineManager->Init(shaderManager);
|
||||
OITDrawer::Init(samplerManager, allocator, screenPipelineManager.get());
|
||||
|
||||
if (descriptorSets.size() > GetContext()->GetSwapChainSize())
|
||||
descriptorSets.resize(GetContext()->GetSwapChainSize());
|
||||
else
|
||||
while (descriptorSets.size() < GetContext()->GetSwapChainSize())
|
||||
{
|
||||
descriptorSets.push_back(OITDescriptorSets());
|
||||
descriptorSets.back().Init(samplerManager,
|
||||
screenPipelineManager->GetPipelineLayout(),
|
||||
screenPipelineManager->GetPerFrameDSLayout(),
|
||||
screenPipelineManager->GetPerPolyDSLayout(),
|
||||
screenPipelineManager->GetPass1DSLayout(),
|
||||
screenPipelineManager->GetPass2DSLayout());
|
||||
}
|
||||
}
|
||||
OITScreenDrawer() = default;
|
||||
OITScreenDrawer(const OITScreenDrawer& other) = delete;
|
||||
OITScreenDrawer(OITScreenDrawer&& other) = default;
|
||||
OITScreenDrawer& operator=(const OITScreenDrawer& other) = delete;
|
||||
OITScreenDrawer& operator=(OITScreenDrawer&& other) = default;
|
||||
|
||||
virtual vk::CommandBuffer BeginRenderPass() override;
|
||||
virtual void EndRenderPass() override
|
||||
{
|
||||
GetContext()->EndFrame();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual OITDescriptorSets& GetCurrentDescSet() override { return descriptorSets[GetCurrentImage()]; }
|
||||
virtual BufferData* GetMainBuffer(u32 size) override
|
||||
{
|
||||
if (mainBuffers.empty())
|
||||
{
|
||||
for (int i = 0; i < GetContext()->GetSwapChainSize(); i++)
|
||||
mainBuffers.push_back(std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(),
|
||||
std::max(512 * 1024u, size),
|
||||
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eUniformBuffer
|
||||
| vk::BufferUsageFlagBits::eStorageBuffer)));
|
||||
}
|
||||
else if (mainBuffers[GetCurrentImage()]->bufferSize < size)
|
||||
{
|
||||
u32 newSize = mainBuffers[GetCurrentImage()]->bufferSize;
|
||||
while (newSize < size)
|
||||
newSize *= 2;
|
||||
INFO_LOG(RENDERER, "Increasing main buffer size %d -> %d", (u32)mainBuffers[GetCurrentImage()]->bufferSize, newSize);
|
||||
mainBuffers[GetCurrentImage()] = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(), newSize,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eUniformBuffer
|
||||
| vk::BufferUsageFlagBits::eStorageBuffer));
|
||||
}
|
||||
return mainBuffers[GetCurrentImage()].get();
|
||||
};
|
||||
|
||||
private:
|
||||
int GetCurrentImage() { return GetContext()->GetCurrentImageIndex(); }
|
||||
|
||||
std::vector<OITDescriptorSets> descriptorSets;
|
||||
std::vector<std::unique_ptr<BufferData>> mainBuffers;
|
||||
std::unique_ptr<OITPipelineManager> screenPipelineManager;
|
||||
};
|
||||
|
||||
class OITTextureDrawer : public OITDrawer
|
||||
{
|
||||
public:
|
||||
void Init(SamplerManager *samplerManager, Allocator *allocator, RttOITPipelineManager *pipelineManager, TextureCache *textureCache)
|
||||
{
|
||||
OITDrawer::Init(samplerManager, allocator, pipelineManager);
|
||||
|
||||
descriptorSets.Init(samplerManager,
|
||||
pipelineManager->GetPipelineLayout(),
|
||||
pipelineManager->GetPerFrameDSLayout(),
|
||||
pipelineManager->GetPerPolyDSLayout(),
|
||||
pipelineManager->GetPass1DSLayout(),
|
||||
pipelineManager->GetPass2DSLayout());
|
||||
fence = GetContext()->GetDevice().createFenceUnique(vk::FenceCreateInfo());
|
||||
this->textureCache = textureCache;
|
||||
}
|
||||
void SetCommandPool(CommandPool *commandPool) { this->commandPool = commandPool; }
|
||||
|
||||
OITTextureDrawer() = default;
|
||||
OITTextureDrawer(const OITTextureDrawer& other) = delete;
|
||||
OITTextureDrawer(OITTextureDrawer&& other) = default;
|
||||
OITTextureDrawer& operator=(const OITTextureDrawer& other) = delete;
|
||||
OITTextureDrawer& operator=(OITTextureDrawer&& other) = default;
|
||||
virtual void EndRenderPass() override;
|
||||
|
||||
protected:
|
||||
virtual vk::CommandBuffer BeginRenderPass() override;
|
||||
OITDescriptorSets& GetCurrentDescSet() override { return descriptorSets; }
|
||||
|
||||
virtual BufferData* GetMainBuffer(u32 size) override
|
||||
{
|
||||
if (!mainBuffer || mainBuffer->bufferSize < size)
|
||||
{
|
||||
u32 newSize = mainBuffer ? mainBuffer->bufferSize : 128 * 1024u;
|
||||
while (newSize < size)
|
||||
newSize *= 2;
|
||||
INFO_LOG(RENDERER, "Increasing RTT main buffer size %d -> %d", !mainBuffer ? 0 : (u32)mainBuffer->bufferSize, newSize);
|
||||
mainBuffer = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(), newSize,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eUniformBuffer
|
||||
| vk::BufferUsageFlagBits::eStorageBuffer, allocator));
|
||||
}
|
||||
return mainBuffer.get();
|
||||
}
|
||||
|
||||
private:
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 textureAddr = 0;
|
||||
|
||||
Texture *texture = nullptr;
|
||||
vk::Image colorImage;
|
||||
vk::CommandBuffer currentCommandBuffer;
|
||||
vk::UniqueFramebuffer framebuffer;
|
||||
std::unique_ptr<FramebufferAttachment> colorAttachment;
|
||||
std::unique_ptr<FramebufferAttachment> depthAttachment;
|
||||
vk::UniqueFence fence;
|
||||
|
||||
OITDescriptorSets descriptorSets;
|
||||
std::unique_ptr<BufferData> mainBuffer;
|
||||
CommandPool *commandPool = nullptr;
|
||||
TextureCache *textureCache = nullptr;
|
||||
};
|
|
@ -0,0 +1,636 @@
|
|||
/*
|
||||
Created on: Nov 6, 2019
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "oit_pipeline.h"
|
||||
#include "quad.h"
|
||||
|
||||
void OITPipelineManager::MakeRenderPass()
|
||||
{
|
||||
vk::AttachmentDescription attachmentDescriptions[] = {
|
||||
// Swap chain image
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetColorFormat(), vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR),
|
||||
// OP+PT color attachment
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetColorFormat(), vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal),
|
||||
// OP+PT depth attachment
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal),
|
||||
// new depth attachment
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal),
|
||||
};
|
||||
vk::AttachmentReference swapChainReference(0, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
vk::AttachmentReference colorReference(1, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
vk::AttachmentReference depthReference(2, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
vk::AttachmentReference depth2Reference(3, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
|
||||
vk::AttachmentReference depthInput(2, vk::ImageLayout::eShaderReadOnlyOptimal); // eShaderReadOnlyOptimal really?
|
||||
vk::AttachmentReference colorInput(1, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
vk::AttachmentReference unused(VK_ATTACHMENT_UNUSED, vk::ImageLayout::eUndefined);
|
||||
|
||||
vk::SubpassDescription subpasses[] = {
|
||||
// Depth and modvol pass FIXME subpass 0 shouldn't reference the color attachment
|
||||
vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr, 1, &colorReference, nullptr, &depthReference),
|
||||
// Color pass
|
||||
vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 1, &depthInput, 1, &colorReference, nullptr, &depth2Reference),
|
||||
// Final pass
|
||||
vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 1, &colorInput, 1, &swapChainReference, nullptr, &unused),
|
||||
};
|
||||
|
||||
vk::SubpassDependency dependencies[] = {
|
||||
vk::SubpassDependency(VK_SUBPASS_EXTERNAL, 0, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eEarlyFragmentTests,
|
||||
vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead,
|
||||
vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
vk::DependencyFlagBits::eByRegion),
|
||||
vk::SubpassDependency(VK_SUBPASS_EXTERNAL, 1, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::AccessFlagBits::eInputAttachmentRead, vk::AccessFlagBits::eColorAttachmentWrite, vk::DependencyFlagBits::eByRegion),
|
||||
// vk::SubpassDependency(VK_SUBPASS_EXTERNAL, 0, vk::PipelineStageFlagBits::eBottomOfPipe, vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
// vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eColorAttachmentRead, vk::DependencyFlagBits::eByRegion),
|
||||
vk::SubpassDependency(0, 1, vk::PipelineStageFlagBits::eLateFragmentTests, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead, vk::DependencyFlagBits::eByRegion),
|
||||
vk::SubpassDependency(1, 2, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eInputAttachmentRead, vk::DependencyFlagBits::eByRegion),
|
||||
vk::SubpassDependency(2, 2, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eShaderRead, vk::AccessFlagBits::eShaderWrite, vk::DependencyFlagBits::eByRegion),
|
||||
vk::SubpassDependency(2, VK_SUBPASS_EXTERNAL, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eBottomOfPipe,
|
||||
vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eMemoryRead, vk::DependencyFlagBits::eByRegion),
|
||||
};
|
||||
|
||||
renderPass = GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(),
|
||||
ARRAY_SIZE(attachmentDescriptions), attachmentDescriptions,
|
||||
ARRAY_SIZE(subpasses), subpasses,
|
||||
ARRAY_SIZE(dependencies), dependencies));
|
||||
}
|
||||
|
||||
void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, int pass)
|
||||
{
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo();
|
||||
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
|
||||
vk::PrimitiveTopology::eTriangleStrip);
|
||||
|
||||
// Viewport and scissor states
|
||||
vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
|
||||
|
||||
// Rasterization and multisample states
|
||||
vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo
|
||||
(
|
||||
vk::PipelineRasterizationStateCreateFlags(), // flags
|
||||
false, // depthClampEnable
|
||||
false, // rasterizerDiscardEnable
|
||||
vk::PolygonMode::eFill, // polygonMode
|
||||
pp.isp.CullMode == 3 ? vk::CullModeFlagBits::eBack
|
||||
: pp.isp.CullMode == 2 ? vk::CullModeFlagBits::eFront
|
||||
: vk::CullModeFlagBits::eNone, // cullMode
|
||||
vk::FrontFace::eCounterClockwise, // frontFace
|
||||
false, // depthBiasEnable
|
||||
0.0f, // depthBiasConstantFactor
|
||||
0.0f, // depthBiasClamp
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
vk::CompareOp depthOp;
|
||||
if (listType == ListType_Punch_Through || autosort)
|
||||
depthOp = vk::CompareOp::eGreaterOrEqual;
|
||||
else
|
||||
depthOp = depthOps[pp.isp.DepthMode];
|
||||
bool depthWriteEnable;
|
||||
// Z Write Disable seems to be ignored for punch-through.
|
||||
// Fixes Worms World Party, Bust-a-Move 4 and Re-Volt
|
||||
if (listType == ListType_Punch_Through)
|
||||
depthWriteEnable = true;
|
||||
else if (listType == ListType_Translucent)
|
||||
depthWriteEnable = false;
|
||||
else
|
||||
depthWriteEnable = !pp.isp.ZWriteDis;
|
||||
|
||||
bool shadowed = pass == 0 && (listType == ListType_Opaque || listType == ListType_Punch_Through) && pp.pcw.Shadow != 0;
|
||||
vk::StencilOpState stencilOpState;
|
||||
if (shadowed)
|
||||
stencilOpState = vk::StencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eReplace, vk::StencilOp::eKeep, vk::CompareOp::eAlways, 0, 0x80, 0x80);
|
||||
else
|
||||
stencilOpState = vk::StencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eKeep, vk::StencilOp::eKeep, vk::CompareOp::eAlways);
|
||||
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo
|
||||
(
|
||||
vk::PipelineDepthStencilStateCreateFlags(), // flags
|
||||
true, // depthTestEnable
|
||||
depthWriteEnable, // depthWriteEnable
|
||||
depthOp, // depthCompareOp
|
||||
false, // depthBoundTestEnable
|
||||
shadowed, // stencilTestEnable
|
||||
stencilOpState, // front
|
||||
stencilOpState // back
|
||||
);
|
||||
|
||||
// Color flags and blending
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState;
|
||||
// Apparently punch-through polys support blending, or at least some combinations
|
||||
if ((listType == ListType_Punch_Through || pass > 0) && pass != 3)
|
||||
{
|
||||
vk::ColorComponentFlags colorComponentFlags(vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA);
|
||||
u32 src = pp.tsp.SrcInstr;
|
||||
u32 dst = pp.tsp.DstInstr;
|
||||
pipelineColorBlendAttachmentState =
|
||||
{
|
||||
true, // blendEnable
|
||||
getBlendFactor(src, true), // srcColorBlendFactor
|
||||
getBlendFactor(dst, false), // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
getBlendFactor(src, true), // srcAlphaBlendFactor
|
||||
getBlendFactor(dst, false), // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
colorComponentFlags // colorWriteMask
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
pipelineColorBlendAttachmentState =
|
||||
{
|
||||
false, // blendEnable
|
||||
vk::BlendFactor::eZero, // srcColorBlendFactor
|
||||
vk::BlendFactor::eZero, // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
vk::BlendFactor::eZero, // srcAlphaBlendFactor
|
||||
vk::BlendFactor::eZero, // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
vk::ColorComponentFlags() // colorWriteMask
|
||||
};
|
||||
}
|
||||
|
||||
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
|
||||
(
|
||||
vk::PipelineColorBlendStateCreateFlags(), // flags
|
||||
false, // logicOpEnable
|
||||
vk::LogicOp::eNoOp, // logicOp
|
||||
1, // attachmentCount
|
||||
&pipelineColorBlendAttachmentState, // pAttachments
|
||||
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
|
||||
);
|
||||
|
||||
vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates);
|
||||
|
||||
vk::ShaderModule vertex_module = shaderManager->GetVertexShader(OITShaderManager::VertexShaderParams{ pp.pcw.Gouraud == 1 });
|
||||
OITShaderManager::FragmentShaderParams params = {};
|
||||
params.alphaTest = listType == ListType_Punch_Through;
|
||||
params.bumpmap = pp.tcw.PixelFmt == PixelBumpMap;
|
||||
params.clamping = pp.tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff);
|
||||
params.insideClipTest = (pp.tileclip >> 28) == 3;
|
||||
params.fog = settings.rend.Fog ? pp.tsp.FogCtrl : 2;
|
||||
params.gouraud = pp.pcw.Gouraud;
|
||||
params.ignoreTexAlpha = pp.tsp.IgnoreTexA;
|
||||
params.offset = pp.pcw.Offset;
|
||||
params.shaderInstr = pp.tsp.ShadInstr;
|
||||
params.texture = pp.pcw.Texture;
|
||||
//params.trilinear = pp.pcw.Texture && pp.tsp.FilterMode > 1 && listType != ListType_Punch_Through;
|
||||
params.useAlpha = pp.tsp.UseAlpha;
|
||||
params.pass = pass;
|
||||
params.depthFunc = autosort ? 6 : pp.isp.DepthMode;
|
||||
params.twoVolume = pp.tsp1.full != -1 || pp.tcw1.full != -1;
|
||||
vk::ShaderModule fragment_module = shaderManager->GetFragmentShader(params);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[] = {
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" },
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" },
|
||||
};
|
||||
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||
(
|
||||
vk::PipelineCreateFlags(), // flags
|
||||
2, // stageCount
|
||||
stages, // pStages
|
||||
&pipelineVertexInputStateCreateInfo, // pVertexInputState
|
||||
&pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
|
||||
nullptr, // pTessellationState
|
||||
&pipelineViewportStateCreateInfo, // pViewportState
|
||||
&pipelineRasterizationStateCreateInfo, // pRasterizationState
|
||||
&pipelineMultisampleStateCreateInfo, // pMultisampleState
|
||||
&pipelineDepthStencilStateCreateInfo, // pDepthStencilState
|
||||
&pipelineColorBlendStateCreateInfo, // pColorBlendState
|
||||
&pipelineDynamicStateCreateInfo, // pDynamicState
|
||||
*pipelineLayout, // layout
|
||||
*renderPass, // renderPass
|
||||
pass == 0 ? 0 : 1 // subpass
|
||||
);
|
||||
|
||||
pipelines[hash(listType, autosort, &pp, pass)] = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(),
|
||||
graphicsPipelineCreateInfo);
|
||||
}
|
||||
|
||||
void OITPipelineManager::CreateFinalPipeline(bool autosort)
|
||||
{
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetQuadInputStateCreateInfo(false);
|
||||
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
|
||||
vk::PrimitiveTopology::eTriangleStrip);
|
||||
|
||||
// Viewport and scissor states
|
||||
vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
|
||||
|
||||
// Rasterization and multisample states
|
||||
vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo
|
||||
(
|
||||
vk::PipelineRasterizationStateCreateFlags(), // flags
|
||||
false, // depthClampEnable
|
||||
false, // rasterizerDiscardEnable
|
||||
vk::PolygonMode::eFill, // polygonMode
|
||||
vk::CullModeFlagBits::eNone, // cullMode
|
||||
vk::FrontFace::eCounterClockwise, // frontFace
|
||||
false, // depthBiasEnable
|
||||
0.0f, // depthBiasConstantFactor
|
||||
0.0f, // depthBiasClamp
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
|
||||
|
||||
// Color flags and blending
|
||||
vk::ColorComponentFlags colorComponentFlags(vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA);
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState =
|
||||
{
|
||||
false, // blendEnable
|
||||
vk::BlendFactor::eZero, // srcColorBlendFactor
|
||||
vk::BlendFactor::eZero, // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
vk::BlendFactor::eZero, // srcAlphaBlendFactor
|
||||
vk::BlendFactor::eZero, // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
colorComponentFlags // colorWriteMask
|
||||
};
|
||||
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
|
||||
(
|
||||
vk::PipelineColorBlendStateCreateFlags(), // flags
|
||||
false, // logicOpEnable
|
||||
vk::LogicOp::eNoOp, // logicOp
|
||||
1, // attachmentCount
|
||||
&pipelineColorBlendAttachmentState, // pAttachments
|
||||
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
|
||||
);
|
||||
|
||||
vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates);
|
||||
|
||||
vk::ShaderModule vertex_module = shaderManager->GetFinalVertexShader();
|
||||
vk::ShaderModule fragment_module = shaderManager->GetFinalShader(autosort);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[] = {
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" },
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" },
|
||||
};
|
||||
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||
(
|
||||
vk::PipelineCreateFlags(), // flags
|
||||
2, // stageCount
|
||||
stages, // pStages
|
||||
&pipelineVertexInputStateCreateInfo, // pVertexInputState
|
||||
&pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
|
||||
nullptr, // pTessellationState
|
||||
&pipelineViewportStateCreateInfo, // pViewportState
|
||||
&pipelineRasterizationStateCreateInfo, // pRasterizationState
|
||||
&pipelineMultisampleStateCreateInfo, // pMultisampleState
|
||||
&pipelineDepthStencilStateCreateInfo, // pDepthStencilState
|
||||
&pipelineColorBlendStateCreateInfo, // pColorBlendState
|
||||
&pipelineDynamicStateCreateInfo, // pDynamicState
|
||||
*pipelineLayout, // layout
|
||||
*renderPass, // renderPass
|
||||
2 // subpass
|
||||
);
|
||||
|
||||
if (autosort)
|
||||
finalAutosortPipeline = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
|
||||
else
|
||||
finalNosortPipeline = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
|
||||
|
||||
}
|
||||
|
||||
void OITPipelineManager::CreateClearPipeline()
|
||||
{
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetQuadInputStateCreateInfo(false);
|
||||
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
|
||||
vk::PrimitiveTopology::eTriangleStrip);
|
||||
|
||||
// Viewport and scissor states
|
||||
vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
|
||||
|
||||
// Rasterization and multisample states
|
||||
vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo
|
||||
(
|
||||
vk::PipelineRasterizationStateCreateFlags(), // flags
|
||||
false, // depthClampEnable
|
||||
false, // rasterizerDiscardEnable
|
||||
vk::PolygonMode::eFill, // polygonMode
|
||||
vk::CullModeFlagBits::eNone, // cullMode
|
||||
vk::FrontFace::eCounterClockwise, // frontFace
|
||||
false, // depthBiasEnable
|
||||
0.0f, // depthBiasConstantFactor
|
||||
0.0f, // depthBiasClamp
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
|
||||
|
||||
// Color flags and blending
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState;
|
||||
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
|
||||
(
|
||||
vk::PipelineColorBlendStateCreateFlags(), // flags
|
||||
false, // logicOpEnable
|
||||
vk::LogicOp::eNoOp, // logicOp
|
||||
1, // attachmentCount
|
||||
&pipelineColorBlendAttachmentState, // pAttachments
|
||||
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
|
||||
);
|
||||
|
||||
vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates);
|
||||
|
||||
vk::ShaderModule vertex_module = shaderManager->GetFinalVertexShader();
|
||||
vk::ShaderModule fragment_module = shaderManager->GetClearShader();
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[] = {
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" },
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" },
|
||||
};
|
||||
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||
(
|
||||
vk::PipelineCreateFlags(), // flags
|
||||
2, // stageCount
|
||||
stages, // pStages
|
||||
&pipelineVertexInputStateCreateInfo, // pVertexInputState
|
||||
&pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
|
||||
nullptr, // pTessellationState
|
||||
&pipelineViewportStateCreateInfo, // pViewportState
|
||||
&pipelineRasterizationStateCreateInfo, // pRasterizationState
|
||||
&pipelineMultisampleStateCreateInfo, // pMultisampleState
|
||||
&pipelineDepthStencilStateCreateInfo, // pDepthStencilState
|
||||
&pipelineColorBlendStateCreateInfo, // pColorBlendState
|
||||
&pipelineDynamicStateCreateInfo, // pDynamicState
|
||||
*pipelineLayout, // layout
|
||||
*renderPass, // renderPass
|
||||
2 // subpass
|
||||
);
|
||||
|
||||
clearPipeline = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
|
||||
}
|
||||
|
||||
void OITPipelineManager::CreateModVolPipeline(ModVolMode mode)
|
||||
{
|
||||
verify(mode != ModVolMode::Final);
|
||||
|
||||
static const vk::VertexInputBindingDescription vertexBindingDescriptions[] =
|
||||
{
|
||||
{ 0, sizeof(float) * 3 },
|
||||
};
|
||||
static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] =
|
||||
{
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, 0), // pos
|
||||
};
|
||||
// Vertex input state
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo(
|
||||
vk::PipelineVertexInputStateCreateFlags(),
|
||||
ARRAY_SIZE(vertexBindingDescriptions),
|
||||
vertexBindingDescriptions,
|
||||
ARRAY_SIZE(vertexInputAttributeDescriptions),
|
||||
vertexInputAttributeDescriptions);
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
|
||||
vk::PrimitiveTopology::eTriangleList);
|
||||
|
||||
// Viewport and scissor states
|
||||
vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
|
||||
|
||||
// Rasterization and multisample states
|
||||
vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo
|
||||
(
|
||||
vk::PipelineRasterizationStateCreateFlags(), // flags
|
||||
false, // depthClampEnable
|
||||
false, // rasterizerDiscardEnable
|
||||
vk::PolygonMode::eFill, // polygonMode
|
||||
vk::CullModeFlagBits::eNone, // cullMode
|
||||
vk::FrontFace::eCounterClockwise, // frontFace
|
||||
false, // depthBiasEnable
|
||||
0.0f, // depthBiasConstantFactor
|
||||
0.0f, // depthBiasClamp
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
vk::StencilOpState stencilOpState;
|
||||
switch (mode)
|
||||
{
|
||||
case ModVolMode::Xor:
|
||||
stencilOpState = vk::StencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eInvert, vk::StencilOp::eKeep, vk::CompareOp::eAlways, 0, 2, 2);
|
||||
break;
|
||||
case ModVolMode::Or:
|
||||
stencilOpState = vk::StencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eReplace, vk::StencilOp::eKeep, vk::CompareOp::eAlways, 2, 2, 2);
|
||||
break;
|
||||
case ModVolMode::Inclusion:
|
||||
stencilOpState = vk::StencilOpState(vk::StencilOp::eZero, vk::StencilOp::eReplace, vk::StencilOp::eZero, vk::CompareOp::eLessOrEqual, 3, 3, 1);
|
||||
break;
|
||||
case ModVolMode::Exclusion:
|
||||
stencilOpState = vk::StencilOpState(vk::StencilOp::eZero, vk::StencilOp::eKeep, vk::StencilOp::eZero, vk::CompareOp::eEqual, 3, 3, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo
|
||||
(
|
||||
vk::PipelineDepthStencilStateCreateFlags(), // flags
|
||||
mode == ModVolMode::Xor || mode == ModVolMode::Or, // depthTestEnable
|
||||
false, // depthWriteEnable
|
||||
vk::CompareOp::eGreater, // depthCompareOp
|
||||
false, // depthBoundTestEnable
|
||||
true, // stencilTestEnable
|
||||
stencilOpState, // front
|
||||
stencilOpState // back
|
||||
);
|
||||
|
||||
// Color flags and blending
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
|
||||
false, // blendEnable
|
||||
vk::BlendFactor::eZero, // srcColorBlendFactor
|
||||
vk::BlendFactor::eZero, // dstColorBlendFactor
|
||||
vk::BlendOp::eAdd, // colorBlendOp
|
||||
vk::BlendFactor::eZero, // srcAlphaBlendFactor
|
||||
vk::BlendFactor::eZero, // dstAlphaBlendFactor
|
||||
vk::BlendOp::eAdd, // alphaBlendOp
|
||||
vk::ColorComponentFlags() // colorWriteMask
|
||||
);
|
||||
|
||||
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
|
||||
(
|
||||
vk::PipelineColorBlendStateCreateFlags(), // flags
|
||||
false, // logicOpEnable
|
||||
vk::LogicOp::eNoOp, // logicOp
|
||||
1, // attachmentCount
|
||||
&pipelineColorBlendAttachmentState, // pAttachments
|
||||
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
|
||||
);
|
||||
|
||||
vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates);
|
||||
|
||||
vk::ShaderModule vertex_module = shaderManager->GetModVolVertexShader();
|
||||
vk::ShaderModule fragment_module = shaderManager->GetModVolShader();
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[] = {
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" },
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" },
|
||||
};
|
||||
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||
(
|
||||
vk::PipelineCreateFlags(), // flags
|
||||
2, // stageCount
|
||||
stages, // pStages
|
||||
&pipelineVertexInputStateCreateInfo, // pVertexInputState
|
||||
&pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
|
||||
nullptr, // pTessellationState
|
||||
&pipelineViewportStateCreateInfo, // pViewportState
|
||||
&pipelineRasterizationStateCreateInfo, // pRasterizationState
|
||||
&pipelineMultisampleStateCreateInfo, // pMultisampleState
|
||||
&pipelineDepthStencilStateCreateInfo, // pDepthStencilState
|
||||
&pipelineColorBlendStateCreateInfo, // pColorBlendState
|
||||
&pipelineDynamicStateCreateInfo, // pDynamicState
|
||||
*pipelineLayout, // layout
|
||||
*renderPass, // renderPass
|
||||
0 // subpass
|
||||
);
|
||||
|
||||
if (modVolPipelines.empty())
|
||||
modVolPipelines.resize((size_t)ModVolMode::Final);
|
||||
modVolPipelines[(size_t)mode] =
|
||||
GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(),
|
||||
graphicsPipelineCreateInfo);
|
||||
}
|
||||
|
||||
void OITPipelineManager::CreateTrModVolPipeline(ModVolMode mode)
|
||||
{
|
||||
verify(mode != ModVolMode::Final);
|
||||
|
||||
static const vk::VertexInputBindingDescription vertexBindingDescriptions[] =
|
||||
{
|
||||
{ 0, sizeof(float) * 3 },
|
||||
};
|
||||
static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] =
|
||||
{
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, 0), // pos
|
||||
};
|
||||
// Vertex input state
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo(
|
||||
vk::PipelineVertexInputStateCreateFlags(),
|
||||
ARRAY_SIZE(vertexBindingDescriptions),
|
||||
vertexBindingDescriptions,
|
||||
ARRAY_SIZE(vertexInputAttributeDescriptions),
|
||||
vertexInputAttributeDescriptions);
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
|
||||
vk::PrimitiveTopology::eTriangleList);
|
||||
|
||||
// Viewport and scissor states
|
||||
vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
|
||||
|
||||
// Rasterization and multisample states
|
||||
vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo
|
||||
(
|
||||
vk::PipelineRasterizationStateCreateFlags(), // flags
|
||||
false, // depthClampEnable
|
||||
false, // rasterizerDiscardEnable
|
||||
vk::PolygonMode::eFill, // polygonMode
|
||||
vk::CullModeFlagBits::eNone, // cullMode
|
||||
vk::FrontFace::eCounterClockwise, // frontFace
|
||||
false, // depthBiasEnable
|
||||
0.0f, // depthBiasConstantFactor
|
||||
0.0f, // depthBiasClamp
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
|
||||
|
||||
// Color flags and blending
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState;
|
||||
|
||||
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
|
||||
(
|
||||
vk::PipelineColorBlendStateCreateFlags(), // flags
|
||||
false, // logicOpEnable
|
||||
vk::LogicOp::eNoOp, // logicOp
|
||||
1, // attachmentCount
|
||||
&pipelineColorBlendAttachmentState, // pAttachments
|
||||
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
|
||||
);
|
||||
|
||||
vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates);
|
||||
|
||||
vk::ShaderModule vertex_module = shaderManager->GetModVolVertexShader();
|
||||
vk::ShaderModule fragment_module = shaderManager->GetTrModVolShader(mode);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[] = {
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" },
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" },
|
||||
};
|
||||
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||
(
|
||||
vk::PipelineCreateFlags(), // flags
|
||||
2, // stageCount
|
||||
stages, // pStages
|
||||
&pipelineVertexInputStateCreateInfo, // pVertexInputState
|
||||
&pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
|
||||
nullptr, // pTessellationState
|
||||
&pipelineViewportStateCreateInfo, // pViewportState
|
||||
&pipelineRasterizationStateCreateInfo, // pRasterizationState
|
||||
&pipelineMultisampleStateCreateInfo, // pMultisampleState
|
||||
&pipelineDepthStencilStateCreateInfo, // pDepthStencilState
|
||||
&pipelineColorBlendStateCreateInfo, // pColorBlendState
|
||||
&pipelineDynamicStateCreateInfo, // pDynamicState
|
||||
*pipelineLayout, // layout
|
||||
*renderPass, // renderPass
|
||||
2 // subpass
|
||||
);
|
||||
|
||||
if (trModVolPipelines.empty())
|
||||
trModVolPipelines.resize((size_t)ModVolMode::Final);
|
||||
trModVolPipelines[(size_t)mode] =
|
||||
GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(),
|
||||
graphicsPipelineCreateInfo);
|
||||
}
|
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
Created on: Nov 6, 2019
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <tuple>
|
||||
#include <glm/glm.hpp>
|
||||
#include "vulkan.h"
|
||||
#include "oit_shaders.h"
|
||||
#include "texture.h"
|
||||
#include "hw/pvr/ta_ctx.h"
|
||||
|
||||
class OITDescriptorSets
|
||||
{
|
||||
public:
|
||||
// std140 alignment required
|
||||
struct VertexShaderUniforms
|
||||
{
|
||||
glm::mat4 normal_matrix;
|
||||
};
|
||||
|
||||
// std140 alignment required
|
||||
struct FragmentShaderUniforms
|
||||
{
|
||||
float colorClampMin[4];
|
||||
float colorClampMax[4];
|
||||
float sp_FOG_COL_RAM[4]; // Only using 3 elements but easier for std140
|
||||
float sp_FOG_COL_VERT[4]; // same comment
|
||||
float cp_AlphaTestValue;
|
||||
float sp_FOG_DENSITY;
|
||||
float extra_depth_scale;
|
||||
float shade_scale_factor; // new for OIT
|
||||
};
|
||||
|
||||
struct PushConstants
|
||||
{
|
||||
glm::vec4 clipTest;
|
||||
glm::ivec4 blend_mode0; // Only using 2 elements but easier for std140
|
||||
float trilinearAlpha;
|
||||
int pp_Number;
|
||||
int _pad[2];
|
||||
|
||||
// two volume mode
|
||||
glm::ivec4 blend_mode1; // Only using 2 elements but easier for std140
|
||||
int shading_instr0;
|
||||
int shading_instr1;
|
||||
int fog_control0;
|
||||
int fog_control1;
|
||||
int use_alpha0;
|
||||
int use_alpha1;
|
||||
int ignore_tex_alpha0;
|
||||
int ignore_tex_alpha1;
|
||||
};
|
||||
|
||||
void Init(SamplerManager* samplerManager, vk::PipelineLayout pipelineLayout, vk::DescriptorSetLayout perFrameLayout,
|
||||
vk::DescriptorSetLayout perPolyLayout, vk::DescriptorSetLayout pass1Layout, vk::DescriptorSetLayout pass2Layout)
|
||||
{
|
||||
this->samplerManager = samplerManager;
|
||||
this->pipelineLayout = pipelineLayout;
|
||||
this->perFrameLayout = perFrameLayout;
|
||||
this->perPolyLayout = perPolyLayout;
|
||||
this->pass1Layout = pass1Layout;
|
||||
this->pass2Layout = pass2Layout;
|
||||
}
|
||||
// FIXME way too many params
|
||||
void UpdateUniforms(vk::Buffer buffer, u32 vertexUniformOffset, u32 fragmentUniformOffset, vk::ImageView fogImageView,
|
||||
vk::Buffer pixelBuffer, vk::DeviceSize pixelBufferSize, vk::Buffer pixelCounterBuffer, u32 polyParamsOffset,
|
||||
u32 polyParamsSize, vk::ImageView pointerImageView)
|
||||
{
|
||||
if (!perFrameDescSet)
|
||||
{
|
||||
perFrameDescSet = std::move(GetContext()->GetDevice().allocateDescriptorSetsUnique(
|
||||
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &perFrameLayout)).front());
|
||||
}
|
||||
std::vector<vk::DescriptorBufferInfo> bufferInfos;
|
||||
bufferInfos.push_back(vk::DescriptorBufferInfo(buffer, vertexUniformOffset, sizeof(VertexShaderUniforms)));
|
||||
bufferInfos.push_back(vk::DescriptorBufferInfo(buffer, fragmentUniformOffset, sizeof(FragmentShaderUniforms)));
|
||||
|
||||
std::vector<vk::WriteDescriptorSet> writeDescriptorSets;
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*perFrameDescSet, 0, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &bufferInfos[0], nullptr));
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*perFrameDescSet, 1, 0, 1, vk::DescriptorType::eUniformBuffer, nullptr, &bufferInfos[1], nullptr));
|
||||
if (fogImageView)
|
||||
{
|
||||
TSP fogTsp = {};
|
||||
fogTsp.FilterMode = 1;
|
||||
fogTsp.ClampU = 1;
|
||||
fogTsp.ClampV = 1;
|
||||
vk::Sampler fogSampler = samplerManager->GetSampler(fogTsp);
|
||||
static vk::DescriptorImageInfo imageInfo;
|
||||
imageInfo = { fogSampler, fogImageView, vk::ImageLayout::eShaderReadOnlyOptimal };
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*perFrameDescSet, 2, 0, 1, vk::DescriptorType::eCombinedImageSampler, &imageInfo, nullptr, nullptr));
|
||||
}
|
||||
vk::DescriptorBufferInfo pixelBufferInfo(pixelBuffer, 0, pixelBufferSize);
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*perFrameDescSet, 3, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &pixelBufferInfo, nullptr));
|
||||
vk::DescriptorBufferInfo pixelCounterBufferInfo(pixelCounterBuffer, 0, 4);
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*perFrameDescSet, 4, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &pixelCounterBufferInfo, nullptr));
|
||||
vk::DescriptorImageInfo pointerImageInfo(vk::Sampler(), pointerImageView, vk::ImageLayout::eGeneral);
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*perFrameDescSet, 5, 0, 1, vk::DescriptorType::eStorageImage, &pointerImageInfo, nullptr, nullptr));
|
||||
if (polyParamsSize > 0)
|
||||
{
|
||||
static vk::DescriptorBufferInfo polyParamsBufferInfo;
|
||||
polyParamsBufferInfo = vk::DescriptorBufferInfo(buffer, polyParamsOffset, polyParamsSize);
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*perFrameDescSet, 6, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &polyParamsBufferInfo, nullptr));
|
||||
}
|
||||
|
||||
GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);
|
||||
}
|
||||
|
||||
void UpdatePass1Uniforms(vk::ImageView stencilImageView, vk::ImageView depthImageView)
|
||||
{
|
||||
if (!pass1DescSet)
|
||||
{
|
||||
pass1DescSet = std::move(GetContext()->GetDevice().allocateDescriptorSetsUnique(
|
||||
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &pass1Layout)).front());
|
||||
}
|
||||
std::vector<vk::WriteDescriptorSet> writeDescriptorSets;
|
||||
vk::DescriptorImageInfo stencilImageInfo(vk::Sampler(), stencilImageView, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*pass1DescSet, 0, 0, 1, vk::DescriptorType::eInputAttachment, &stencilImageInfo, nullptr, nullptr));
|
||||
vk::DescriptorImageInfo depthImageInfo(vk::Sampler(), depthImageView, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*pass1DescSet, 1, 0, 1, vk::DescriptorType::eInputAttachment, &depthImageInfo, nullptr, nullptr));
|
||||
|
||||
GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);
|
||||
}
|
||||
|
||||
void UpdatePass2Uniforms(vk::ImageView colorImageView)
|
||||
{
|
||||
if (!pass2DescSet)
|
||||
{
|
||||
pass2DescSet = std::move(GetContext()->GetDevice().allocateDescriptorSetsUnique(
|
||||
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), 1, &pass2Layout)).front());
|
||||
}
|
||||
std::vector<vk::WriteDescriptorSet> writeDescriptorSets;
|
||||
vk::DescriptorImageInfo colorImageInfo(vk::Sampler(), colorImageView, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*pass2DescSet, 0, 0, 1, vk::DescriptorType::eInputAttachment, &colorImageInfo, nullptr, nullptr));
|
||||
|
||||
GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);
|
||||
}
|
||||
|
||||
void SetTexture(u64 textureId0, TSP tsp0, u64 textureId1, TSP tsp1)
|
||||
{
|
||||
auto index = std::make_tuple(textureId0, tsp0.full & SamplerManager::TSP_Mask,
|
||||
textureId1, tsp1.full & SamplerManager::TSP_Mask);
|
||||
if (perPolyDescSetsInFlight.find(index) != perPolyDescSetsInFlight.end())
|
||||
return;
|
||||
|
||||
if (perPolyDescSets.empty())
|
||||
{
|
||||
std::vector<vk::DescriptorSetLayout> layouts(10, perPolyLayout);
|
||||
perPolyDescSets = GetContext()->GetDevice().allocateDescriptorSetsUnique(
|
||||
vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), layouts.size(), &layouts[0]));
|
||||
}
|
||||
Texture *texture = reinterpret_cast<Texture *>(textureId0);
|
||||
vk::DescriptorImageInfo imageInfo0(samplerManager->GetSampler(tsp0), texture->GetImageView(), vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
|
||||
std::vector<vk::WriteDescriptorSet> writeDescriptorSets;
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*perPolyDescSets.back(), 0, 0, 1, vk::DescriptorType::eCombinedImageSampler, &imageInfo0, nullptr, nullptr));
|
||||
|
||||
if (textureId1 != -1)
|
||||
{
|
||||
Texture *texture1 = reinterpret_cast<Texture *>(textureId1);
|
||||
vk::DescriptorImageInfo imageInfo1(samplerManager->GetSampler(tsp1), texture1->GetImageView(), vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
|
||||
writeDescriptorSets.push_back(vk::WriteDescriptorSet(*perPolyDescSets.back(), 1, 0, 1, vk::DescriptorType::eCombinedImageSampler, &imageInfo1, nullptr, nullptr));
|
||||
}
|
||||
GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);
|
||||
perPolyDescSetsInFlight[index] = std::move(perPolyDescSets.back());
|
||||
perPolyDescSets.pop_back();
|
||||
}
|
||||
|
||||
void BindPerFrameDescriptorSets(vk::CommandBuffer cmdBuffer)
|
||||
{
|
||||
cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, 1, &perFrameDescSet.get(), 0, nullptr);
|
||||
}
|
||||
|
||||
void BindPass1DescriptorSets(vk::CommandBuffer cmdBuffer)
|
||||
{
|
||||
cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 2, 1, &pass1DescSet.get(), 0, nullptr);
|
||||
}
|
||||
|
||||
void BindPass2DescriptorSets(vk::CommandBuffer cmdBuffer)
|
||||
{
|
||||
cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 3, 1, &pass2DescSet.get(), 0, nullptr);
|
||||
}
|
||||
|
||||
void BindPerPolyDescriptorSets(vk::CommandBuffer cmdBuffer, u64 textureId0, TSP tsp0, u64 textureId1, TSP tsp1)
|
||||
{
|
||||
auto index = std::make_tuple(textureId0, tsp0.full & SamplerManager::TSP_Mask, textureId1, tsp1.full & SamplerManager::TSP_Mask);
|
||||
cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 1, 1,
|
||||
&perPolyDescSetsInFlight[index].get(), 0, nullptr);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
for (auto& pair : perPolyDescSetsInFlight)
|
||||
perPolyDescSets.emplace_back(std::move(pair.second));
|
||||
perPolyDescSetsInFlight.clear();
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
|
||||
|
||||
vk::DescriptorSetLayout perFrameLayout;
|
||||
vk::DescriptorSetLayout perPolyLayout;
|
||||
vk::DescriptorSetLayout pass1Layout;
|
||||
vk::DescriptorSetLayout pass2Layout;
|
||||
vk::PipelineLayout pipelineLayout;
|
||||
|
||||
vk::UniqueDescriptorSet perFrameDescSet;
|
||||
vk::UniqueDescriptorSet pass1DescSet;
|
||||
vk::UniqueDescriptorSet pass2DescSet;
|
||||
std::vector<vk::UniqueDescriptorSet> perPolyDescSets;
|
||||
std::map<std::tuple<u64, u32, u64, u32>, vk::UniqueDescriptorSet> perPolyDescSetsInFlight;
|
||||
|
||||
SamplerManager* samplerManager;
|
||||
};
|
||||
|
||||
class OITPipelineManager
|
||||
{
|
||||
public:
|
||||
virtual ~OITPipelineManager() = default;
|
||||
|
||||
virtual void Init(OITShaderManager *shaderManager)
|
||||
{
|
||||
this->shaderManager = shaderManager;
|
||||
|
||||
if (!perFrameLayout)
|
||||
{
|
||||
// Descriptor set and pipeline layout
|
||||
vk::DescriptorSetLayoutBinding perFrameBindings[] = {
|
||||
{ 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex }, // vertex uniforms
|
||||
{ 1, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // fragment uniforms
|
||||
{ 2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment },// fog texture
|
||||
{ 3, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // pixel buffer
|
||||
{ 4, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // pixel counter
|
||||
{ 5, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment }, // a-buffer pointers
|
||||
{ 6, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // Tr poly params
|
||||
};
|
||||
perFrameLayout = GetContext()->GetDevice().createDescriptorSetLayoutUnique(
|
||||
vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(perFrameBindings), perFrameBindings));
|
||||
|
||||
vk::DescriptorSetLayoutBinding pass1Bindings[] = {
|
||||
{ 0, vk::DescriptorType::eInputAttachment, 1, vk::ShaderStageFlagBits::eFragment }, // stencil input attachment
|
||||
{ 1, vk::DescriptorType::eInputAttachment, 1, vk::ShaderStageFlagBits::eFragment }, // depth input attachment
|
||||
};
|
||||
pass1Layout = GetContext()->GetDevice().createDescriptorSetLayoutUnique(
|
||||
vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(pass1Bindings), pass1Bindings));
|
||||
|
||||
vk::DescriptorSetLayoutBinding pass2Bindings[] = {
|
||||
{ 0, vk::DescriptorType::eInputAttachment, 1, vk::ShaderStageFlagBits::eFragment }, // color input attachment
|
||||
};
|
||||
pass2Layout = GetContext()->GetDevice().createDescriptorSetLayoutUnique(
|
||||
vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(pass2Bindings), pass2Bindings));
|
||||
|
||||
vk::DescriptorSetLayoutBinding perPolyBindings[] = {
|
||||
{ 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment },// texture 0
|
||||
{ 1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment },// texture 1 (for 2-volume mode)
|
||||
};
|
||||
perPolyLayout = GetContext()->GetDevice().createDescriptorSetLayoutUnique(
|
||||
vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(perPolyBindings), perPolyBindings));
|
||||
|
||||
vk::PushConstantRange pushConstant(vk::ShaderStageFlagBits::eFragment, 0, sizeof(OITDescriptorSets::PushConstants));
|
||||
vk::DescriptorSetLayout layouts[] = { *perFrameLayout, *perPolyLayout, *pass1Layout, *pass2Layout };
|
||||
pipelineLayout = GetContext()->GetDevice().createPipelineLayoutUnique(
|
||||
vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), ARRAY_SIZE(layouts), layouts, 1, &pushConstant));
|
||||
}
|
||||
|
||||
MakeRenderPass();
|
||||
pipelines.clear();
|
||||
modVolPipelines.clear();
|
||||
}
|
||||
|
||||
vk::Pipeline GetPipeline(u32 listType, bool autosort, const PolyParam& pp, int pass)
|
||||
{
|
||||
u32 pipehash = hash(listType, autosort, &pp, pass);
|
||||
const auto &pipeline = pipelines.find(pipehash);
|
||||
if (pipeline != pipelines.end())
|
||||
return pipeline->second.get();
|
||||
|
||||
CreatePipeline(listType, autosort, pp, pass);
|
||||
|
||||
return *pipelines[pipehash];
|
||||
}
|
||||
|
||||
vk::Pipeline GetModifierVolumePipeline(ModVolMode mode)
|
||||
{
|
||||
if (modVolPipelines.empty() || !modVolPipelines[(size_t)mode])
|
||||
CreateModVolPipeline(mode);
|
||||
return *modVolPipelines[(size_t)mode];
|
||||
}
|
||||
vk::Pipeline GetTrModifierVolumePipeline(ModVolMode mode)
|
||||
{
|
||||
if (trModVolPipelines.empty() || !trModVolPipelines[(size_t)mode])
|
||||
CreateTrModVolPipeline(mode);
|
||||
return *trModVolPipelines[(size_t)mode];
|
||||
}
|
||||
vk::Pipeline GetFinalPipeline(bool autosort)
|
||||
{
|
||||
vk::UniquePipeline& pipeline = autosort ? finalAutosortPipeline : finalNosortPipeline;
|
||||
if (!pipeline)
|
||||
CreateFinalPipeline(autosort);
|
||||
return *pipeline;
|
||||
}
|
||||
vk::Pipeline GetClearPipeline()
|
||||
{
|
||||
if (!clearPipeline)
|
||||
CreateClearPipeline();
|
||||
return *clearPipeline;
|
||||
}
|
||||
vk::PipelineLayout GetPipelineLayout() const { return *pipelineLayout; }
|
||||
vk::DescriptorSetLayout GetPerFrameDSLayout() const { return *perFrameLayout; }
|
||||
vk::DescriptorSetLayout GetPerPolyDSLayout() const { return *perPolyLayout; }
|
||||
vk::DescriptorSetLayout GetPass1DSLayout() const { return *pass1Layout; }
|
||||
vk::DescriptorSetLayout GetPass2DSLayout() const { return *pass2Layout; }
|
||||
|
||||
vk::RenderPass GetRenderPass() const { return *renderPass; }
|
||||
|
||||
private:
|
||||
void MakeRenderPass();
|
||||
void CreateModVolPipeline(ModVolMode mode);
|
||||
void CreateTrModVolPipeline(ModVolMode mode);
|
||||
|
||||
u32 hash(u32 listType, bool autosort, const PolyParam *pp, int pass) const
|
||||
{
|
||||
u32 hash = pp->pcw.Gouraud | (pp->pcw.Offset << 1) | (pp->pcw.Texture << 2) | (pp->pcw.Shadow << 3)
|
||||
| (((pp->tileclip >> 28) == 3) << 4);
|
||||
hash |= ((listType >> 1) << 5);
|
||||
if (pp->tcw1.full != -1 || pp->tsp1.full != -1)
|
||||
{
|
||||
// Two-volume mode
|
||||
hash |= (1 << 31) | (pp->tsp.ColorClamp << 11);
|
||||
}
|
||||
else
|
||||
{
|
||||
hash |= (pp->tsp.ShadInstr << 7) | (pp->tsp.IgnoreTexA << 9) | (pp->tsp.UseAlpha << 10)
|
||||
| (pp->tsp.ColorClamp << 11) | ((settings.rend.Fog ? pp->tsp.FogCtrl : 2) << 12)
|
||||
| (pp->tsp.SrcInstr << 14) | (pp->tsp.DstInstr << 17);
|
||||
}
|
||||
hash |= (pp->isp.ZWriteDis << 20) | (pp->isp.CullMode << 21) | ((autosort ? 6 : pp->isp.DepthMode) << 23);
|
||||
hash |= (u32)pass << 26;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo GetMainVertexInputStateCreateInfo(bool full = true) const
|
||||
{
|
||||
// Vertex input state
|
||||
static const vk::VertexInputBindingDescription vertexBindingDescriptions[] =
|
||||
{
|
||||
{ 0, sizeof(Vertex) },
|
||||
};
|
||||
static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] =
|
||||
{
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
|
||||
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, col)), // base color
|
||||
vk::VertexInputAttributeDescription(2, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, spc)), // offset color
|
||||
vk::VertexInputAttributeDescription(3, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, u)), // tex coord
|
||||
vk::VertexInputAttributeDescription(4, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, col1)), // base1 color
|
||||
vk::VertexInputAttributeDescription(5, 0, vk::Format::eR8G8B8A8Uint, offsetof(Vertex, spc1)), // offset1 color
|
||||
vk::VertexInputAttributeDescription(6, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, u1)), // tex1 coord
|
||||
};
|
||||
static const vk::VertexInputAttributeDescription vertexInputLightAttributeDescriptions[] =
|
||||
{
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, x)), // pos
|
||||
};
|
||||
return vk::PipelineVertexInputStateCreateInfo(
|
||||
vk::PipelineVertexInputStateCreateFlags(),
|
||||
ARRAY_SIZE(vertexBindingDescriptions),
|
||||
vertexBindingDescriptions,
|
||||
full ? ARRAY_SIZE(vertexInputAttributeDescriptions) : ARRAY_SIZE(vertexInputLightAttributeDescriptions),
|
||||
full ? vertexInputAttributeDescriptions : vertexInputLightAttributeDescriptions);
|
||||
}
|
||||
|
||||
void CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, int pass);
|
||||
void CreateFinalPipeline(bool autosort);
|
||||
void CreateClearPipeline();
|
||||
|
||||
std::map<u32, vk::UniquePipeline> pipelines;
|
||||
std::vector<vk::UniquePipeline> modVolPipelines;
|
||||
std::vector<vk::UniquePipeline> trModVolPipelines;
|
||||
vk::UniquePipeline finalAutosortPipeline;
|
||||
vk::UniquePipeline finalNosortPipeline;
|
||||
vk::UniquePipeline clearPipeline;
|
||||
|
||||
vk::UniquePipelineLayout pipelineLayout;
|
||||
vk::UniqueDescriptorSetLayout perFrameLayout;
|
||||
vk::UniqueDescriptorSetLayout pass1Layout;
|
||||
vk::UniqueDescriptorSetLayout pass2Layout;
|
||||
vk::UniqueDescriptorSetLayout perPolyLayout;
|
||||
|
||||
protected:
|
||||
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
|
||||
|
||||
vk::UniqueRenderPass renderPass;
|
||||
OITShaderManager *shaderManager;
|
||||
};
|
||||
|
||||
class RttOITPipelineManager : public OITPipelineManager
|
||||
{
|
||||
public:
|
||||
void Init(OITShaderManager *shaderManager) override
|
||||
{
|
||||
OITPipelineManager::Init(shaderManager);
|
||||
|
||||
// FIXME RTT render pass
|
||||
renderToTextureBuffer = settings.rend.RenderToTextureBuffer;
|
||||
vk::AttachmentDescription attachmentDescriptions[] = {
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
renderToTextureBuffer ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal),
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal),
|
||||
};
|
||||
vk::AttachmentReference colorReference(0, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
vk::AttachmentReference depthReference(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
vk::SubpassDescription subpass(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr, 1, &colorReference, nullptr, &depthReference);
|
||||
vk::SubpassDependency dependencies[] {
|
||||
vk::SubpassDependency(VK_SUBPASS_EXTERNAL, 0, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::AccessFlagBits::eShaderRead, vk::AccessFlagBits::eColorAttachmentWrite),
|
||||
vk::SubpassDependency(0, VK_SUBPASS_EXTERNAL, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eShaderRead),
|
||||
};
|
||||
vk::SubpassDependency vramWriteDeps[] {
|
||||
vk::SubpassDependency(0, VK_SUBPASS_EXTERNAL,
|
||||
vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eTransfer | vk::PipelineStageFlagBits::eHost,
|
||||
vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eHostRead),
|
||||
};
|
||||
|
||||
renderPass = GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(), 2, attachmentDescriptions,
|
||||
1, &subpass, renderToTextureBuffer ? ARRAY_SIZE(vramWriteDeps) : ARRAY_SIZE(dependencies), renderToTextureBuffer ? vramWriteDeps : dependencies));
|
||||
}
|
||||
void CheckSettingsChange()
|
||||
{
|
||||
if (renderToTextureBuffer != settings.rend.RenderToTextureBuffer)
|
||||
Init(shaderManager);
|
||||
}
|
||||
|
||||
private:
|
||||
bool renderToTextureBuffer;
|
||||
};
|
|
@ -0,0 +1,368 @@
|
|||
#include "vulkan.h"
|
||||
#include "oit_shaders.h"
|
||||
|
||||
const vk::DeviceSize PixelBufferSize = 512 * 1024 * 1024;
|
||||
|
||||
vk::UniquePipelineLayout pipelineLayout;
|
||||
vk::UniqueDescriptorSetLayout perFrameLayout;
|
||||
vk::UniqueDescriptorSetLayout perPolyLayout;
|
||||
vk::UniqueRenderPass renderPass;
|
||||
|
||||
vk::ImageView colorImageView;
|
||||
vk::ImageView depthImageView;
|
||||
vk::ImageView stencilImageView;
|
||||
|
||||
std::unique_ptr<FramebufferAttachment> colorAttachment;
|
||||
std::unique_ptr<FramebufferAttachment> depthAttachment;
|
||||
|
||||
std::vector<vk::UniqueFramebuffer> framebuffers;
|
||||
|
||||
std::unique_ptr<BufferData> pixelBuffer;
|
||||
std::unique_ptr<BufferData> pixelCounter;
|
||||
std::unique_ptr<FramebufferAttachment> abufferPointerAttachment;
|
||||
std::unique_ptr<BufferData> trParamBuffer;
|
||||
|
||||
struct PushConstants
|
||||
{
|
||||
glm::vec4 clipTest;
|
||||
glm::ivec2 blend_mode0;
|
||||
float trilinearAlpha;
|
||||
int pp_Number;
|
||||
|
||||
// two volume mode
|
||||
glm::ivec2 blend_mode1;
|
||||
int shading_instr0;
|
||||
int shading_instr1;
|
||||
int fog_control0;
|
||||
int fog_control1;
|
||||
bool use_alpha0;
|
||||
bool use_alpha1;
|
||||
bool ignore_tex_alpha0;
|
||||
bool ignore_tex_alpha1;
|
||||
};
|
||||
|
||||
struct QuadVertex
|
||||
{
|
||||
f32 pos[3];
|
||||
f32 uv[2];
|
||||
};
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo GetQuadInputStateCreateInfo(bool uv)
|
||||
{
|
||||
// Vertex input state
|
||||
static const vk::VertexInputBindingDescription vertexBindingDescriptions[] =
|
||||
{
|
||||
{ 0, sizeof(QuadVertex) },
|
||||
};
|
||||
static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] =
|
||||
{
|
||||
vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(QuadVertex, pos)), // pos
|
||||
vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32Sfloat, offsetof(QuadVertex, uv)), // tex coord
|
||||
};
|
||||
return vk::PipelineVertexInputStateCreateInfo(
|
||||
vk::PipelineVertexInputStateCreateFlags(),
|
||||
ARRAY_SIZE(vertexBindingDescriptions),
|
||||
vertexBindingDescriptions,
|
||||
ARRAY_SIZE(vertexInputAttributeDescriptions) - (uv ? 0 : 1),
|
||||
vertexInputAttributeDescriptions);
|
||||
}
|
||||
|
||||
static VulkanContext *GetContext()
|
||||
{
|
||||
return VulkanContext::Instance();
|
||||
}
|
||||
|
||||
void makeRenderPass()
|
||||
{
|
||||
vk::AttachmentDescription attachmentDescriptions[] = {
|
||||
// Swap chain image
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetColorFormat(), vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR),
|
||||
// OP+PT color attachment
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetColorFormat(), vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal),
|
||||
// OP+PT depth attachment
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal),
|
||||
// new depth attachment
|
||||
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
|
||||
vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal),
|
||||
};
|
||||
vk::AttachmentReference swapChainReference(0, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
vk::AttachmentReference colorReference(1, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
vk::AttachmentReference depthReference(2, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
vk::AttachmentReference depth2Reference(3, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
|
||||
vk::AttachmentReference depthReadReference(2, vk::ImageLayout::eDepthStencilReadOnlyOptimal);
|
||||
vk::AttachmentReference colorReadReference(1, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
|
||||
vk::SubpassDescription subpasses[] = {
|
||||
// Depth and modvol pass
|
||||
vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr, 0, nullptr, nullptr, &depthReference),
|
||||
// Color pass
|
||||
vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 1, &depthReadReference, 1, &colorReference, nullptr, &depth2Reference),
|
||||
// Final pass
|
||||
vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 1, &colorReadReference, 1, &swapChainReference, nullptr, nullptr),
|
||||
};
|
||||
|
||||
vk::SubpassDependency dependencies[] {
|
||||
vk::SubpassDependency(VK_SUBPASS_EXTERNAL, 0, vk::PipelineStageFlagBits::eBottomOfPipe, vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eColorAttachmentRead, vk::DependencyFlagBits::eByRegion),
|
||||
vk::SubpassDependency(0, 1, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eShaderRead, vk::DependencyFlagBits::eByRegion),
|
||||
vk::SubpassDependency(1, 2, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eFragmentShader,
|
||||
vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eShaderRead, vk::DependencyFlagBits::eByRegion),
|
||||
vk::SubpassDependency(2, VK_SUBPASS_EXTERNAL, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eBottomOfPipe,
|
||||
vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eMemoryRead, vk::DependencyFlagBits::eByRegion),
|
||||
};
|
||||
|
||||
renderPass = GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(),
|
||||
ARRAY_SIZE(attachmentDescriptions), attachmentDescriptions,
|
||||
ARRAY_SIZE(subpasses), subpasses,
|
||||
ARRAY_SIZE(dependencies), dependencies));
|
||||
}
|
||||
|
||||
void makeAttachments(int width, int height, Allocator *allocator)
|
||||
{
|
||||
colorAttachment = std::unique_ptr<FramebufferAttachment>(new FramebufferAttachment(GetContext()->GetPhysicalDevice(),
|
||||
GetContext()->GetDevice(), allocator));
|
||||
colorAttachment->Init(width, height, GetContext()->GetColorFormat(), vk::ImageUsageFlagBits::eInputAttachment);
|
||||
colorImageView = colorAttachment->GetImageView();
|
||||
|
||||
depthAttachment = std::unique_ptr<FramebufferAttachment>(new FramebufferAttachment(GetContext()->GetPhysicalDevice(),
|
||||
GetContext()->GetDevice(), allocator));
|
||||
depthAttachment->Init(width, height, GetContext()->GetDepthFormat(), vk::ImageUsageFlagBits::eInputAttachment);
|
||||
depthImageView = depthAttachment->GetImageView();
|
||||
stencilImageView = depthAttachment->GetStencilView();
|
||||
}
|
||||
|
||||
void makeFramebuffers(int width, int height, vk::RenderPass renderPass)
|
||||
{
|
||||
vk::ImageView attachments[] = {
|
||||
nullptr, // swap chain image view, set later
|
||||
colorImageView,
|
||||
depthImageView,
|
||||
stencilImageView
|
||||
};
|
||||
framebuffers.reserve(GetContext()->GetSwapChainSize());
|
||||
for (int i = 0; i < GetContext()->GetSwapChainSize(); i++)
|
||||
{
|
||||
vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), renderPass, 4, attachments, width, height, 1);
|
||||
attachments[0] = GetContext()->GetSwapChainImageView(i);
|
||||
framebuffers.push_back(GetContext()->GetDevice().createFramebufferUnique(createInfo));
|
||||
}
|
||||
}
|
||||
|
||||
void makeBuffers(int width, int height, int trParamBufSize, Allocator *allocator)
|
||||
{
|
||||
pixelBuffer = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(), PixelBufferSize,
|
||||
vk::BufferUsageFlagBits::eStorageBuffer, &SimpleAllocator::instance, vk::MemoryPropertyFlagBits::eDeviceLocal));
|
||||
pixelCounter = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(), 4,
|
||||
vk::BufferUsageFlagBits::eStorageBuffer, allocator, vk::MemoryPropertyFlagBits::eDeviceLocal));
|
||||
trParamBuffer = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(), trParamBufSize,
|
||||
vk::BufferUsageFlagBits::eStorageBuffer, allocator));
|
||||
abufferPointerAttachment = std::unique_ptr<FramebufferAttachment>(new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(),
|
||||
allocator));
|
||||
abufferPointerAttachment->Init(width, height, vk::Format::eR32Uint);
|
||||
}
|
||||
|
||||
void makeDescSetLayout()
|
||||
{
|
||||
vk::DescriptorSetLayoutBinding perFrameBindings[] = {
|
||||
{ 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex }, // vertex uniforms
|
||||
{ 1, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // fragment uniforms
|
||||
{ 2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment },// fog texture
|
||||
{ 3, vk::DescriptorType::eInputAttachment, 1, vk::ShaderStageFlagBits::eFragment }, // stencil input attachment
|
||||
{ 4, vk::DescriptorType::eInputAttachment, 1, vk::ShaderStageFlagBits::eFragment }, // depth input attachment
|
||||
{ 5, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // pixel buffer
|
||||
{ 6, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // pixel counter
|
||||
{ 7, vk::DescriptorType::eStorageImage, 1, vk::ShaderStageFlagBits::eFragment }, // a-buffer pointers
|
||||
{ 8, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // Tr poly params
|
||||
};
|
||||
perFrameLayout = GetContext()->GetDevice().createDescriptorSetLayoutUnique(
|
||||
vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(perFrameBindings), perFrameBindings));
|
||||
|
||||
vk::DescriptorSetLayoutBinding perPolyBindings[] = {
|
||||
{ 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment },// texture 0
|
||||
{ 1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment },// texture 1 (for 2-volume mode)
|
||||
};
|
||||
perPolyLayout = GetContext()->GetDevice().createDescriptorSetLayoutUnique(
|
||||
vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), ARRAY_SIZE(perFrameBindings), perFrameBindings));
|
||||
vk::PushConstantRange pushConstant(vk::ShaderStageFlagBits::eFragment, 0, sizeof(PushConstants));
|
||||
vk::DescriptorSetLayout layouts[] = { *perFrameLayout, *perPolyLayout };
|
||||
pipelineLayout = GetContext()->GetDevice().createPipelineLayoutUnique(
|
||||
vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), ARRAY_SIZE(layouts), layouts, 1, &pushConstant));
|
||||
}
|
||||
|
||||
void makeFinalPipeline(bool depthSorted, OITShaderManager *shaderManager)
|
||||
{
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetQuadInputStateCreateInfo();
|
||||
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
|
||||
vk::PrimitiveTopology::eTriangleStrip);
|
||||
|
||||
// Viewport and scissor states
|
||||
vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
|
||||
|
||||
// Rasterization and multisample states
|
||||
vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo
|
||||
(
|
||||
vk::PipelineRasterizationStateCreateFlags(), // flags
|
||||
false, // depthClampEnable
|
||||
false, // rasterizerDiscardEnable
|
||||
vk::PolygonMode::eFill, // polygonMode
|
||||
vk::CullModeFlagBits::eNone, // cullMode
|
||||
vk::FrontFace::eCounterClockwise, // frontFace
|
||||
false, // depthBiasEnable
|
||||
0.0f, // depthBiasConstantFactor
|
||||
0.0f, // depthBiasClamp
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
|
||||
|
||||
// Color flags and blending
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState;
|
||||
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
|
||||
(
|
||||
vk::PipelineColorBlendStateCreateFlags(), // flags
|
||||
false, // logicOpEnable
|
||||
vk::LogicOp::eNoOp, // logicOp
|
||||
1, // attachmentCount
|
||||
&pipelineColorBlendAttachmentState, // pAttachments
|
||||
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
|
||||
);
|
||||
|
||||
vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates);
|
||||
|
||||
vk::ShaderModule vertex_module = shaderManager->GetFinalVertexShader();
|
||||
vk::ShaderModule fragment_module = shaderManager->GetFinalShader(depthSorted);
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[] = {
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" },
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" },
|
||||
};
|
||||
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||
(
|
||||
vk::PipelineCreateFlags(), // flags
|
||||
2, // stageCount
|
||||
stages, // pStages
|
||||
&pipelineVertexInputStateCreateInfo, // pVertexInputState
|
||||
&pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
|
||||
nullptr, // pTessellationState
|
||||
&pipelineViewportStateCreateInfo, // pViewportState
|
||||
&pipelineRasterizationStateCreateInfo, // pRasterizationState
|
||||
&pipelineMultisampleStateCreateInfo, // pMultisampleState
|
||||
&pipelineDepthStencilStateCreateInfo, // pDepthStencilState
|
||||
&pipelineColorBlendStateCreateInfo, // pColorBlendState
|
||||
&pipelineDynamicStateCreateInfo, // pDynamicState
|
||||
*pipelineLayout, // layout
|
||||
*renderPass, // renderPass
|
||||
2 // subpass
|
||||
);
|
||||
|
||||
// TODO store...
|
||||
GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
|
||||
|
||||
}
|
||||
|
||||
void makeClearPipeline(OITShaderManager *shaderManager)
|
||||
{
|
||||
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetQuadInputStateCreateInfo();
|
||||
|
||||
// Input assembly state
|
||||
vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(),
|
||||
vk::PrimitiveTopology::eTriangleStrip);
|
||||
|
||||
// Viewport and scissor states
|
||||
vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr);
|
||||
|
||||
// Rasterization and multisample states
|
||||
vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo
|
||||
(
|
||||
vk::PipelineRasterizationStateCreateFlags(), // flags
|
||||
false, // depthClampEnable
|
||||
false, // rasterizerDiscardEnable
|
||||
vk::PolygonMode::eFill, // polygonMode
|
||||
vk::CullModeFlagBits::eNone, // cullMode
|
||||
vk::FrontFace::eCounterClockwise, // frontFace
|
||||
false, // depthBiasEnable
|
||||
0.0f, // depthBiasConstantFactor
|
||||
0.0f, // depthBiasClamp
|
||||
0.0f, // depthBiasSlopeFactor
|
||||
1.0f // lineWidth
|
||||
);
|
||||
vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo;
|
||||
|
||||
// Depth and stencil
|
||||
vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo;
|
||||
|
||||
// Color flags and blending
|
||||
vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState;
|
||||
vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo
|
||||
(
|
||||
vk::PipelineColorBlendStateCreateFlags(), // flags
|
||||
false, // logicOpEnable
|
||||
vk::LogicOp::eNoOp, // logicOp
|
||||
1, // attachmentCount
|
||||
&pipelineColorBlendAttachmentState, // pAttachments
|
||||
{ { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants
|
||||
);
|
||||
|
||||
vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
||||
vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates);
|
||||
|
||||
vk::ShaderModule vertex_module = shaderManager->GetFinalVertexShader();
|
||||
vk::ShaderModule fragment_module = shaderManager->GetClearShader();
|
||||
|
||||
vk::PipelineShaderStageCreateInfo stages[] = {
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" },
|
||||
{ vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" },
|
||||
};
|
||||
vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo
|
||||
(
|
||||
vk::PipelineCreateFlags(), // flags
|
||||
2, // stageCount
|
||||
stages, // pStages
|
||||
&pipelineVertexInputStateCreateInfo, // pVertexInputState
|
||||
&pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState
|
||||
nullptr, // pTessellationState
|
||||
&pipelineViewportStateCreateInfo, // pViewportState
|
||||
&pipelineRasterizationStateCreateInfo, // pRasterizationState
|
||||
&pipelineMultisampleStateCreateInfo, // pMultisampleState
|
||||
&pipelineDepthStencilStateCreateInfo, // pDepthStencilState
|
||||
&pipelineColorBlendStateCreateInfo, // pColorBlendState
|
||||
&pipelineDynamicStateCreateInfo, // pDynamicState
|
||||
*pipelineLayout, // layout
|
||||
*renderPass, // renderPass
|
||||
2 // subpass
|
||||
);
|
||||
|
||||
// TODO store...
|
||||
GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo);
|
||||
|
||||
}
|
||||
|
||||
void InitOIT(Allocator *allocator)
|
||||
{
|
||||
const int width = (int)lroundf((float)GetContext()->GetViewPort().width * settings.rend.ScreenScaling / 100);
|
||||
const int height = (int)lroundf((float)GetContext()->GetViewPort().height * settings.rend.ScreenScaling / 100);
|
||||
makeRenderPass();
|
||||
makeDescSetLayout();
|
||||
makeBuffers(width, height, 2 * 1024 * 1024, allocator);
|
||||
makeAttachments(width, height, allocator);
|
||||
makeFramebuffers(width, height, *renderPass);
|
||||
OITShaderManager shaderManager;
|
||||
makeFinalPipeline(true, &shaderManager);
|
||||
makeClearPipeline(&shaderManager);
|
||||
printf("YO BIATCH!!\n");
|
||||
}
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
Created on: Nov 7, 2019
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <memory>
|
||||
#include <math.h>
|
||||
#include "vulkan.h"
|
||||
#include "hw/pvr/Renderer_if.h"
|
||||
#include "allocator.h"
|
||||
#include "commandpool.h"
|
||||
#include "oit_drawer.h"
|
||||
#include "oit_shaders.h"
|
||||
#include "rend/gui.h"
|
||||
#include "rend/osd.h"
|
||||
#include "pipeline.h"
|
||||
|
||||
class OITVulkanRenderer : public Renderer
|
||||
{
|
||||
public:
|
||||
bool Init() override
|
||||
{
|
||||
DEBUG_LOG(RENDERER, "OITVulkanRenderer::Init");
|
||||
texCommandPool.Init();
|
||||
|
||||
// FIXME this might be called after initial init
|
||||
texAllocator.SetChunkSize(16 * 1024 * 1024);
|
||||
rttPipelineManager.Init(&shaderManager);
|
||||
//TODO if (textureDrawer.size() > GetContext()->GetSwapChainSize())
|
||||
// textureDrawer.resize(GetContext()->GetSwapChainSize());
|
||||
// else
|
||||
// {
|
||||
// while (textureDrawer.size() < GetContext()->GetSwapChainSize())
|
||||
// textureDrawer.emplace_back();
|
||||
// }
|
||||
// for (auto& drawer : textureDrawer)
|
||||
// {
|
||||
// drawer.Init(&samplerManager, &texAllocator, &rttPipelineManager, &textureCache);
|
||||
// drawer.SetCommandPool(&texCommandPool);
|
||||
// }
|
||||
|
||||
screenDrawer.Init(&samplerManager, &texAllocator, &shaderManager);
|
||||
quadPipeline.Init(&normalShaderManager);
|
||||
quadBuffer = std::unique_ptr<QuadBuffer>(new QuadBuffer(&texAllocator));
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (!vjoyTexture)
|
||||
{
|
||||
int w, h;
|
||||
u8 *image_data = loadPNGData(get_readonly_data_path(DATA_PATH "buttons.png"), w, h);
|
||||
if (image_data == nullptr)
|
||||
{
|
||||
WARN_LOG(RENDERER, "Cannot load buttons.png image");
|
||||
}
|
||||
else
|
||||
{
|
||||
vjoyTexture = std::unique_ptr<Texture>(new Texture());
|
||||
vjoyTexture->tex_type = TextureType::_8888;
|
||||
vjoyTexture->tcw.full = 0;
|
||||
vjoyTexture->tsp.full = 0;
|
||||
vjoyTexture->SetAllocator(&texAllocator);
|
||||
vjoyTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
|
||||
vjoyTexture->SetDevice(GetContext()->GetDevice());
|
||||
vjoyTexture->SetCommandBuffer(texCommandPool.Allocate());
|
||||
vjoyTexture->UploadToGPU(OSD_TEX_W, OSD_TEX_H, image_data);
|
||||
vjoyTexture->SetCommandBuffer(nullptr);
|
||||
delete [] image_data;
|
||||
osdPipeline.Init(&normalShaderManager, vjoyTexture->GetImageView());
|
||||
}
|
||||
}
|
||||
if (!osdBuffer)
|
||||
{
|
||||
osdBuffer = std::unique_ptr<BufferData>(new BufferData(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice(),
|
||||
sizeof(OSDVertex) * VJOY_VISIBLE * 4,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer, &texAllocator));
|
||||
}
|
||||
#endif
|
||||
// InitOIT(&texAllocator);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Resize(int w, int h) override
|
||||
{
|
||||
texCommandPool.Init();
|
||||
screenDrawer.Init(&samplerManager, &texAllocator, &shaderManager);
|
||||
quadPipeline.Init(&normalShaderManager);
|
||||
osdPipeline.Init(&normalShaderManager, vjoyTexture->GetImageView());
|
||||
}
|
||||
|
||||
void Term() override
|
||||
{
|
||||
DEBUG_LOG(RENDERER, "VulkanRenderer::Term");
|
||||
GetContext()->WaitIdle();
|
||||
quadBuffer = nullptr;
|
||||
textureCache.Clear();
|
||||
fogTexture = nullptr;
|
||||
texCommandPool.Term();
|
||||
framebufferTextures.clear();
|
||||
}
|
||||
|
||||
bool RenderFramebuffer()
|
||||
{
|
||||
if (FB_R_SIZE.fb_x_size == 0 || FB_R_SIZE.fb_y_size == 0)
|
||||
return false;
|
||||
|
||||
PixelBuffer<u32> pb;
|
||||
int width;
|
||||
int height;
|
||||
ReadFramebuffer(pb, width, height);
|
||||
|
||||
if (framebufferTextures.size() != GetContext()->GetSwapChainSize())
|
||||
framebufferTextures.resize(GetContext()->GetSwapChainSize());
|
||||
std::unique_ptr<Texture>& curTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()];
|
||||
if (!curTexture)
|
||||
{
|
||||
curTexture = std::unique_ptr<Texture>(new Texture());
|
||||
curTexture->tex_type = TextureType::_8888;
|
||||
curTexture->tcw.full = 0;
|
||||
curTexture->tsp.full = 0;
|
||||
curTexture->SetAllocator(&texAllocator);
|
||||
curTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
|
||||
curTexture->SetDevice(GetContext()->GetDevice());
|
||||
}
|
||||
curTexture->SetCommandBuffer(texCommandPool.Allocate());
|
||||
curTexture->UploadToGPU(width, height, (u8*)pb.data());
|
||||
curTexture->SetCommandBuffer(nullptr);
|
||||
texCommandPool.EndFrame();
|
||||
|
||||
TransformMatrix<false> matrices(pvrrc);
|
||||
glm::vec4 viewport_min = matrices.GetViewportMatrix() * glm::vec4(0, 0, 0, 1.f);
|
||||
glm::vec4 viewport_dim = matrices.GetViewportMatrix() * glm::vec4(640.f, 480.f, 0, 0);
|
||||
|
||||
float min_x = viewport_min[0];
|
||||
float min_y = viewport_min[1];
|
||||
width = viewport_dim[0];
|
||||
height = viewport_dim[1];
|
||||
if (width < 0)
|
||||
{
|
||||
min_x += width;
|
||||
width = -width;
|
||||
}
|
||||
if (height < 0)
|
||||
{
|
||||
min_y += height;
|
||||
height = -height;
|
||||
}
|
||||
quadBuffer->Update();
|
||||
|
||||
//vk::CommandBuffer cmdBuffer = screenDrawer.BeginRenderPass();
|
||||
GetContext()->NewFrame();
|
||||
GetContext()->BeginRenderPass();
|
||||
vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer();
|
||||
|
||||
vk::Pipeline pipeline = quadPipeline.GetPipeline();
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||
|
||||
quadPipeline.SetTexture(curTexture.get());
|
||||
quadPipeline.BindDescriptorSets(cmdBuffer);
|
||||
|
||||
float blendConstants[4] = { 1.0, 1.0, 1.0, 1.0 };
|
||||
cmdBuffer.setBlendConstants(blendConstants);
|
||||
|
||||
vk::Viewport viewport(min_x, min_y, width, height);
|
||||
cmdBuffer.setViewport(0, 1, &viewport);
|
||||
cmdBuffer.setScissor(0, vk::Rect2D(
|
||||
vk::Offset2D((u32)std::max(lroundf(min_x), 0L), (u32)std::max(lroundf(min_y), 0L)),
|
||||
vk::Extent2D((u32)std::max(lroundf(width), 0L), (u32)std::max(lroundf(height), 0L))));
|
||||
// cmdBuffer.draw(3, 1, 0, 0);
|
||||
quadBuffer->Bind(cmdBuffer);
|
||||
quadBuffer->Draw(cmdBuffer);
|
||||
|
||||
gui_display_osd();
|
||||
|
||||
GetContext()->EndFrame();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Process(TA_context* ctx) override
|
||||
{
|
||||
// TODO
|
||||
if (ctx->rend.isRTT) return false;
|
||||
texCommandPool.BeginFrame();
|
||||
|
||||
if (ctx->rend.isRenderFramebuffer)
|
||||
{
|
||||
return RenderFramebuffer();
|
||||
}
|
||||
|
||||
ctx->rend_inuse.Lock();
|
||||
|
||||
if (KillTex)
|
||||
textureCache.Clear();
|
||||
|
||||
bool result = ta_parse_vdrc(ctx);
|
||||
|
||||
textureCache.CollectCleanup();
|
||||
|
||||
if (ctx->rend.Overrun)
|
||||
WARN_LOG(PVR, "ERROR: TA context overrun");
|
||||
|
||||
result = result && !ctx->rend.Overrun;
|
||||
|
||||
if (result)
|
||||
CheckFogTexture();
|
||||
|
||||
if (!result || !ctx->rend.isRTT)
|
||||
texCommandPool.EndFrame();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DrawOSD(bool clear_screen) override
|
||||
{
|
||||
gui_display_osd();
|
||||
if (!vjoyTexture)
|
||||
return;
|
||||
if (clear_screen)
|
||||
{
|
||||
GetContext()->NewFrame();
|
||||
GetContext()->BeginRenderPass();
|
||||
}
|
||||
const float dc2s_scale_h = screen_height / 480.0f;
|
||||
const float sidebarWidth = (screen_width - dc2s_scale_h * 640.0f) / 2;
|
||||
|
||||
std::vector<OSDVertex> osdVertices = GetOSDVertices();
|
||||
const float x1 = 2.0f / (screen_width / dc2s_scale_h);
|
||||
const float y1 = 2.0f / 480;
|
||||
const float x2 = 1 - 2 * sidebarWidth / screen_width;
|
||||
const float y2 = 1;
|
||||
for (OSDVertex& vtx : osdVertices)
|
||||
{
|
||||
vtx.x = vtx.x * x1 - x2;
|
||||
vtx.y = vtx.y * y1 - y2;
|
||||
}
|
||||
|
||||
const vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer();
|
||||
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, osdPipeline.GetPipeline());
|
||||
|
||||
osdPipeline.BindDescriptorSets(cmdBuffer);
|
||||
const vk::Viewport viewport(0, 0, (float)screen_width, (float)screen_height, 0, 1.f);
|
||||
cmdBuffer.setViewport(0, 1, &viewport);
|
||||
const vk::Rect2D scissor({ 0, 0 }, { (u32)screen_width, (u32)screen_height });
|
||||
cmdBuffer.setScissor(0, 1, &scissor);
|
||||
osdBuffer->upload(osdVertices.size() * sizeof(OSDVertex), osdVertices.data());
|
||||
const vk::DeviceSize zero = 0;
|
||||
cmdBuffer.bindVertexBuffers(0, 1, &osdBuffer->buffer.get(), &zero);
|
||||
for (int i = 0; i < osdVertices.size(); i += 4)
|
||||
cmdBuffer.draw(4, 1, i, 0);
|
||||
if (clear_screen)
|
||||
GetContext()->EndFrame();
|
||||
}
|
||||
|
||||
bool Render() override
|
||||
{
|
||||
if (pvrrc.isRenderFramebuffer)
|
||||
return true;
|
||||
|
||||
OITDrawer *drawer;
|
||||
if (pvrrc.isRTT)
|
||||
drawer = &textureDrawer[GetContext()->GetCurrentImageIndex()];
|
||||
else
|
||||
drawer = &screenDrawer;
|
||||
|
||||
drawer->Draw(fogTexture.get());
|
||||
//TODO if (!pvrrc.isRTT)
|
||||
// DrawOSD(false);
|
||||
drawer->EndRenderPass();
|
||||
|
||||
return !pvrrc.isRTT;
|
||||
}
|
||||
|
||||
void Present() override
|
||||
{
|
||||
GetContext()->Present();
|
||||
}
|
||||
|
||||
virtual u64 GetTexture(TSP tsp, TCW tcw) override
|
||||
{
|
||||
Texture* tf = textureCache.getTextureCacheData(tsp, tcw);
|
||||
|
||||
if (tf->IsNew())
|
||||
{
|
||||
tf->Create();
|
||||
tf->SetAllocator(&texAllocator);
|
||||
tf->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
|
||||
tf->SetDevice(GetContext()->GetDevice());
|
||||
}
|
||||
|
||||
//update if needed
|
||||
if (tf->NeedsUpdate())
|
||||
{
|
||||
tf->SetCommandBuffer(texCommandPool.Allocate());
|
||||
tf->Update();
|
||||
tf->SetCommandBuffer(nullptr);
|
||||
}
|
||||
else
|
||||
tf->CheckCustomTexture();
|
||||
|
||||
return tf->GetIntId();
|
||||
}
|
||||
|
||||
private:
|
||||
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
|
||||
|
||||
void CheckFogTexture()
|
||||
{
|
||||
if (!fogTexture)
|
||||
{
|
||||
fogTexture = std::unique_ptr<Texture>(new Texture());
|
||||
fogTexture->SetAllocator(&texAllocator);
|
||||
fogTexture->SetPhysicalDevice(GetContext()->GetPhysicalDevice());
|
||||
fogTexture->SetDevice(GetContext()->GetDevice());
|
||||
fogTexture->tex_type = TextureType::_8;
|
||||
fog_needs_update = true;
|
||||
}
|
||||
if (!fog_needs_update || !settings.rend.Fog)
|
||||
return;
|
||||
fog_needs_update = false;
|
||||
u8 texData[256];
|
||||
MakeFogTexture(texData);
|
||||
fogTexture->SetCommandBuffer(texCommandPool.Allocate());
|
||||
|
||||
fogTexture->UploadToGPU(128, 2, texData);
|
||||
|
||||
fogTexture->SetCommandBuffer(nullptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<QuadBuffer> quadBuffer;
|
||||
std::unique_ptr<Texture> fogTexture;
|
||||
CommandPool texCommandPool;
|
||||
|
||||
SamplerManager samplerManager;
|
||||
OITShaderManager shaderManager;
|
||||
ShaderManager normalShaderManager;
|
||||
OITScreenDrawer screenDrawer;
|
||||
RttOITPipelineManager rttPipelineManager;
|
||||
std::vector<OITTextureDrawer> textureDrawer;
|
||||
VulkanAllocator texAllocator;
|
||||
std::vector<std::unique_ptr<Texture>> framebufferTextures;
|
||||
QuadPipeline quadPipeline;
|
||||
OSDPipeline osdPipeline;
|
||||
std::unique_ptr<Texture> vjoyTexture;
|
||||
std::unique_ptr<BufferData> osdBuffer;
|
||||
TextureCache textureCache;
|
||||
};
|
||||
|
||||
Renderer* rend_OITVulkan()
|
||||
{
|
||||
return new OITVulkanRenderer();
|
||||
}
|
|
@ -0,0 +1,926 @@
|
|||
/*
|
||||
Created on: Nov 6, 2019
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "oit_shaders.h"
|
||||
#include "compiler.h"
|
||||
|
||||
static const char OITVertexShaderSource[] = R"(
|
||||
#version 450
|
||||
|
||||
#define pp_Gouraud %d
|
||||
|
||||
#if pp_Gouraud == 0
|
||||
#define INTERPOLATION flat
|
||||
#else
|
||||
#define INTERPOLATION smooth
|
||||
#endif
|
||||
|
||||
layout (std140, set = 0, binding = 0) uniform VertexShaderUniforms
|
||||
{
|
||||
mat4 normal_matrix;
|
||||
} uniformBuffer;
|
||||
|
||||
layout (location = 0) in vec4 in_pos;
|
||||
layout (location = 1) in uvec4 in_base;
|
||||
layout (location = 2) in uvec4 in_offs;
|
||||
layout (location = 3) in mediump vec2 in_uv;
|
||||
layout (location = 4) in uvec4 in_base1; // New for OIT, only for OP/PT with 2-volume
|
||||
layout (location = 5) in uvec4 in_offs1;
|
||||
layout (location = 6) in mediump vec2 in_uv1;
|
||||
|
||||
layout (location = 0) INTERPOLATION out lowp vec4 vtx_base;
|
||||
layout (location = 1) INTERPOLATION out lowp vec4 vtx_offs;
|
||||
layout (location = 2) out mediump vec2 vtx_uv;
|
||||
layout (location = 3) INTERPOLATION out lowp vec4 vtx_base1; // New for OIT, only for OP/PT with 2-volume
|
||||
layout (location = 4) INTERPOLATION out lowp vec4 vtx_offs1;
|
||||
layout (location = 5) out mediump vec2 vtx_uv1;
|
||||
|
||||
void main()
|
||||
{
|
||||
vtx_base = vec4(in_base) / 255.0;
|
||||
vtx_offs = vec4(in_offs) / 255.0;
|
||||
vtx_uv = in_uv;
|
||||
vtx_base1 = vec4(in_base1) / 255.0; // New for OIT, only for OP/PT with 2-volume
|
||||
vtx_offs1 = vec4(in_offs1) / 255.0;
|
||||
vtx_uv1 = in_uv1;
|
||||
vec4 vpos = in_pos;
|
||||
if (vpos.z < 0.0 || vpos.z > 3.4e37)
|
||||
{
|
||||
gl_Position = vec4(0.0, 0.0, 1.0, 1.0 / vpos.z);
|
||||
return;
|
||||
}
|
||||
vpos = uniformBuffer.normal_matrix * vpos;
|
||||
vpos.w = 1.0 / vpos.z;
|
||||
vpos.z = vpos.w;
|
||||
vpos.xy *= vpos.w;
|
||||
gl_Position = vpos;
|
||||
}
|
||||
)";
|
||||
|
||||
static const char OITShaderHeader[] = R"(
|
||||
#version 450
|
||||
|
||||
layout (std140, set = 0, binding = 1) uniform FragmentShaderUniforms
|
||||
{
|
||||
vec4 colorClampMin;
|
||||
vec4 colorClampMax;
|
||||
vec4 sp_FOG_COL_RAM;
|
||||
vec4 sp_FOG_COL_VERT;
|
||||
float cp_AlphaTestValue;
|
||||
float sp_FOG_DENSITY;
|
||||
float extra_depth_scale;
|
||||
float shade_scale_factor;
|
||||
} uniformBuffer;
|
||||
|
||||
layout(set = 0, binding = 5, r32ui) uniform coherent restrict uimage2D abufferPointerImg;
|
||||
struct Pixel {
|
||||
vec4 color;
|
||||
float depth;
|
||||
uint seq_num;
|
||||
uint next;
|
||||
};
|
||||
#define EOL 0xFFFFFFFFu
|
||||
layout (set = 0, binding = 3, std430) coherent restrict buffer PixelBuffer_ {
|
||||
Pixel pixels[];
|
||||
} PixelBuffer;
|
||||
layout(set = 0, binding = 4) buffer PixelCounter_ {
|
||||
uint buffer_index;
|
||||
} PixelCounter;
|
||||
|
||||
#define ZERO 0
|
||||
#define ONE 1
|
||||
#define OTHER_COLOR 2
|
||||
#define INVERSE_OTHER_COLOR 3
|
||||
#define SRC_ALPHA 4
|
||||
#define INVERSE_SRC_ALPHA 5
|
||||
#define DST_ALPHA 6
|
||||
#define INVERSE_DST_ALPHA 7
|
||||
|
||||
uint getNextPixelIndex()
|
||||
{
|
||||
uint index = atomicAdd(PixelCounter.buffer_index, 1);
|
||||
if (index >= PixelBuffer.pixels.length())
|
||||
// Buffer overflow
|
||||
discard;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void setFragDepth(void)
|
||||
{
|
||||
float w = 100000.0 * gl_FragCoord.w;
|
||||
gl_FragDepth = log2(1.0 + w) / 34.0;
|
||||
}
|
||||
struct PolyParam {
|
||||
int first;
|
||||
int count;
|
||||
int texid_low;
|
||||
int texid_high;
|
||||
int tsp;
|
||||
int tcw;
|
||||
int pcw;
|
||||
int isp;
|
||||
float zvZ;
|
||||
int tileclip;
|
||||
int tsp1;
|
||||
int tcw1;
|
||||
int texid1_low;
|
||||
int texid1_high;
|
||||
};
|
||||
layout (set = 0, binding = 6, std430) readonly buffer TrPolyParamBuffer {
|
||||
PolyParam tr_poly_params[];
|
||||
} TrPolyParam;
|
||||
|
||||
#define GET_TSP_FOR_AREA int tsp = area1 ? pp.tsp1 : pp.tsp;
|
||||
|
||||
int getSrcBlendFunc(const PolyParam pp, bool area1)
|
||||
{
|
||||
GET_TSP_FOR_AREA
|
||||
return (tsp >> 29) & 7;
|
||||
}
|
||||
|
||||
int getDstBlendFunc(const PolyParam pp, bool area1)
|
||||
{
|
||||
GET_TSP_FOR_AREA
|
||||
return (tsp >> 26) & 7;
|
||||
}
|
||||
|
||||
bool getSrcSelect(const PolyParam pp, bool area1)
|
||||
{
|
||||
GET_TSP_FOR_AREA
|
||||
return ((tsp >> 25) & 1) != 0;
|
||||
}
|
||||
|
||||
bool getDstSelect(const PolyParam pp, bool area1)
|
||||
{
|
||||
GET_TSP_FOR_AREA
|
||||
return ((tsp >> 24) & 1) != 0;
|
||||
}
|
||||
|
||||
int getFogControl(const PolyParam pp, bool area1)
|
||||
{
|
||||
GET_TSP_FOR_AREA
|
||||
return (tsp >> 22) & 3;
|
||||
}
|
||||
|
||||
bool getUseAlpha(const PolyParam pp, bool area1)
|
||||
{
|
||||
GET_TSP_FOR_AREA
|
||||
return ((tsp >> 20) & 1) != 0;
|
||||
}
|
||||
|
||||
bool getIgnoreTexAlpha(const PolyParam pp, bool area1)
|
||||
{
|
||||
GET_TSP_FOR_AREA
|
||||
return ((tsp >> 19) & 1) != 0;
|
||||
}
|
||||
|
||||
int getShadingInstruction(const PolyParam pp, bool area1)
|
||||
{
|
||||
GET_TSP_FOR_AREA
|
||||
return (tsp >> 6) & 3;
|
||||
}
|
||||
|
||||
int getDepthFunc(const PolyParam pp)
|
||||
{
|
||||
return (pp.isp >> 29) & 7;
|
||||
}
|
||||
|
||||
bool getDepthMask(const PolyParam pp)
|
||||
{
|
||||
return ((pp.isp >> 26) & 1) != 1;
|
||||
}
|
||||
|
||||
bool getShadowEnable(const PolyParam pp)
|
||||
{
|
||||
return ((pp.pcw >> 7) & 1) != 0;
|
||||
}
|
||||
|
||||
uint getPolyNumber(const Pixel pixel)
|
||||
{
|
||||
return pixel.seq_num & 0x3FFFFFFFu;
|
||||
}
|
||||
|
||||
#define SHADOW_STENCIL 0x40000000u
|
||||
#define SHADOW_ACC 0x80000000u
|
||||
|
||||
bool isShadowed(const Pixel pixel)
|
||||
{
|
||||
return (pixel.seq_num & SHADOW_ACC) == SHADOW_ACC;
|
||||
}
|
||||
|
||||
bool isTwoVolumes(const PolyParam pp)
|
||||
{
|
||||
return pp.tsp1 != -1 || pp.tcw1 != -1;
|
||||
}
|
||||
)";
|
||||
|
||||
static const char OITFragmentShaderSource[] = R"(
|
||||
#define cp_AlphaTest %d
|
||||
#define pp_ClipInside %d
|
||||
#define pp_UseAlpha %d
|
||||
#define pp_Texture %d
|
||||
#define pp_IgnoreTexA %d
|
||||
#define pp_ShadInstr %d
|
||||
#define pp_Offset %d
|
||||
#define pp_FogCtrl %d
|
||||
#define pp_TwoVolumes %d
|
||||
#define pp_DepthFunc %d
|
||||
#define pp_Gouraud %d
|
||||
#define pp_BumpMap %d
|
||||
#define ColorClamping %d
|
||||
#define PASS %d
|
||||
#define PI 3.1415926
|
||||
|
||||
#if PASS <= 1
|
||||
layout (location = 0) out vec4 FragColor;
|
||||
#define gl_FragColor FragColor
|
||||
#endif
|
||||
|
||||
#if pp_TwoVolumes == 1
|
||||
#define IF(x) if (x)
|
||||
#else
|
||||
#define IF(x)
|
||||
#endif
|
||||
|
||||
#if pp_Gouraud == 0
|
||||
#define INTERPOLATION flat
|
||||
#else
|
||||
#define INTERPOLATION smooth
|
||||
#endif
|
||||
|
||||
layout (push_constant) uniform pushBlock
|
||||
{
|
||||
vec4 clipTest;
|
||||
ivec4 blend_mode0;
|
||||
float trilinearAlpha;
|
||||
int pp_Number;
|
||||
|
||||
// two volume mode
|
||||
ivec4 blend_mode1;
|
||||
int shading_instr0;
|
||||
int shading_instr1;
|
||||
int fog_control0;
|
||||
int fog_control1;
|
||||
int use_alpha0;
|
||||
int use_alpha1;
|
||||
int ignore_tex_alpha0;
|
||||
int ignore_tex_alpha1;
|
||||
} pushConstants;
|
||||
|
||||
#if pp_Texture == 1
|
||||
layout (set = 1, binding = 0) uniform sampler2D tex0;
|
||||
#if pp_TwoVolumes == 1
|
||||
layout (set = 1, binding = 1) uniform sampler2D tex1;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if PASS == 1
|
||||
//layout (set = 2, binding = 0) uniform usampler2D shadow_stencil;
|
||||
layout (input_attachment_index = 0, set = 2, binding = 0) uniform usubpassInput shadow_stencil;
|
||||
#endif
|
||||
#if PASS == 3
|
||||
//layout (set = 2, binding = 1) uniform sampler2D DepthTex;
|
||||
layout (input_attachment_index = 0, set = 2, binding = 1) uniform subpassInput DepthTex;
|
||||
#endif
|
||||
|
||||
// Vertex input
|
||||
layout (location = 0) INTERPOLATION in lowp vec4 vtx_base;
|
||||
layout (location = 1) INTERPOLATION in lowp vec4 vtx_offs;
|
||||
layout (location = 2) in mediump vec2 vtx_uv;
|
||||
layout (location = 3) INTERPOLATION in lowp vec4 vtx_base1; // new for OIT. Only if 2 vol
|
||||
layout (location = 4) INTERPOLATION in lowp vec4 vtx_offs1;
|
||||
layout (location = 5) in mediump vec2 vtx_uv1;
|
||||
|
||||
#if pp_FogCtrl != 2
|
||||
layout (set = 0, binding = 2) uniform sampler2D fog_table;
|
||||
|
||||
float fog_mode2(float w)
|
||||
{
|
||||
float z = clamp(w * uniformBuffer.extra_depth_scale * uniformBuffer.sp_FOG_DENSITY, 1.0, 255.9999);
|
||||
float exp = floor(log2(z));
|
||||
float m = z * 16.0 / pow(2.0, exp) - 16.0;
|
||||
float idx = floor(m) + exp * 16.0 + 0.5;
|
||||
vec4 fog_coef = texture(fog_table, vec2(idx / 128.0, 0.75 - (m - floor(m)) / 2.0));
|
||||
return fog_coef.r;
|
||||
}
|
||||
#endif
|
||||
|
||||
vec4 colorClamp(vec4 col)
|
||||
{
|
||||
// TODO This can change in two-volume mode
|
||||
#if ColorClamping == 1
|
||||
return clamp(col, uniformBuffer.colorClampMin, uniformBuffer.colorClampMax);
|
||||
#else
|
||||
return col;
|
||||
#endif
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
setFragDepth();
|
||||
|
||||
#if PASS == 3
|
||||
// Manual depth testing
|
||||
highp float frontDepth = subpassLoad(DepthTex).r;
|
||||
#if pp_DepthFunc == 0 // Never
|
||||
discard;
|
||||
#elif pp_DepthFunc == 1 // Less
|
||||
if (gl_FragDepth >= frontDepth)
|
||||
discard;
|
||||
#elif pp_DepthFunc == 2 // Equal
|
||||
if (gl_FragDepth != frontDepth)
|
||||
discard;
|
||||
#elif pp_DepthFunc == 3 // Less or equal
|
||||
if (gl_FragDepth > frontDepth)
|
||||
discard;
|
||||
#elif pp_DepthFunc == 4 // Greater
|
||||
if (gl_FragDepth <= frontDepth)
|
||||
discard;
|
||||
#elif pp_DepthFunc == 5 // Not equal
|
||||
if (gl_FragDepth == frontDepth)
|
||||
discard;
|
||||
#elif pp_DepthFunc == 6 // Greater or equal
|
||||
if (gl_FragDepth < frontDepth)
|
||||
discard;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Clip inside the box
|
||||
#if pp_ClipInside == 1
|
||||
if (gl_FragCoord.x >= pushConstants.clipTest.x && gl_FragCoord.x <= pushConstants.clipTest.z
|
||||
&& gl_FragCoord.y >= pushConstants.clipTest.y && gl_FragCoord.y <= pushConstants.clipTest.w)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
highp vec4 color = vtx_base;
|
||||
lowp vec4 offset = vtx_offs;
|
||||
mediump vec2 uv = vtx_uv;
|
||||
bool area1 = false;
|
||||
ivec2 cur_blend_mode = pushConstants.blend_mode0.xy;
|
||||
|
||||
#if pp_TwoVolumes == 1
|
||||
bool cur_use_alpha = pushConstants.use_alpha0 != 0;
|
||||
bool cur_ignore_tex_alpha = pushConstants.ignore_tex_alpha0 != 0;
|
||||
int cur_shading_instr = pushConstants.shading_instr0;
|
||||
int cur_fog_control = pushConstants.fog_control0;
|
||||
#if PASS == 1
|
||||
uvec4 stencil = subpassLoad(shadow_stencil);
|
||||
if (stencil.r == 0x81u) {
|
||||
color = vtx_base1;
|
||||
offset = vtx_offs1;
|
||||
uv = vtx_uv1;
|
||||
area1 = true;
|
||||
cur_blend_mode = pushConstants.blend_mode1.xy;
|
||||
cur_use_alpha = pushConstants.use_alpha1 != 0;
|
||||
cur_ignore_tex_alpha = pushConstants.ignore_tex_alpha1 != 0;
|
||||
cur_shading_instr = pushConstants.shading_instr1;
|
||||
cur_fog_control = pushConstants.fog_control1;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if pp_UseAlpha == 0 || pp_TwoVolumes == 1
|
||||
IF (!cur_use_alpha)
|
||||
color.a = 1.0;
|
||||
#endif
|
||||
#if pp_FogCtrl == 3 || pp_TwoVolumes == 1 // LUT Mode 2
|
||||
IF (cur_fog_control == 3)
|
||||
color = vec4(uniformBuffer.sp_FOG_COL_RAM.rgb, fog_mode2(gl_FragCoord.w));
|
||||
#endif
|
||||
#if pp_Texture==1
|
||||
{
|
||||
highp vec4 texcol;
|
||||
#if pp_TwoVolumes == 1
|
||||
if (area1)
|
||||
texcol = texture(tex1, uv);
|
||||
else
|
||||
#endif
|
||||
texcol = texture(tex0, uv);
|
||||
#if pp_BumpMap == 1
|
||||
highp float s = PI / 2.0 * (texcol.a * 15.0 * 16.0 + texcol.r * 15.0) / 255.0;
|
||||
highp float r = 2.0 * PI * (texcol.g * 15.0 * 16.0 + texcol.b * 15.0) / 255.0;
|
||||
texcol.a = clamp(vtx_offs.a + vtx_offs.r * sin(s) + vtx_offs.g * cos(s) * cos(r - 2.0 * PI * vtx_offs.b), 0.0, 1.0);
|
||||
texcol.rgb = vec3(1.0, 1.0, 1.0);
|
||||
#else
|
||||
#if pp_IgnoreTexA==1 || pp_TwoVolumes == 1
|
||||
IF(cur_ignore_tex_alpha)
|
||||
texcol.a = 1.0;
|
||||
#endif
|
||||
|
||||
#if cp_AlphaTest == 1
|
||||
if (uniformBuffer.cp_AlphaTestValue > texcol.a)
|
||||
discard;
|
||||
#endif
|
||||
#endif
|
||||
#if pp_ShadInstr == 0 || pp_TwoVolumes == 1 // DECAL
|
||||
IF(cur_shading_instr == 0)
|
||||
{
|
||||
color = texcol;
|
||||
}
|
||||
#endif
|
||||
#if pp_ShadInstr == 1 || pp_TwoVolumes == 1 // MODULATE
|
||||
IF(cur_shading_instr == 1)
|
||||
{
|
||||
color.rgb *= texcol.rgb;
|
||||
color.a = texcol.a;
|
||||
}
|
||||
#endif
|
||||
#if pp_ShadInstr == 2 || pp_TwoVolumes == 1 // DECAL ALPHA
|
||||
IF(cur_shading_instr == 2)
|
||||
{
|
||||
color.rgb = mix(color.rgb, texcol.rgb, texcol.a);
|
||||
}
|
||||
#endif
|
||||
#if pp_ShadInstr == 3 || pp_TwoVolumes == 1 // MODULATE ALPHA
|
||||
IF(cur_shading_instr == 3)
|
||||
{
|
||||
color *= texcol;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if pp_Offset == 1 && pp_BumpMap == 0
|
||||
{
|
||||
color.rgb += offset.rgb;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if PASS == 1 && pp_TwoVolumes == 0
|
||||
uvec4 stencil = subpassLoad(shadow_stencil);
|
||||
if (stencil.r == 0x81u)
|
||||
color.rgb *= uniformBuffer.shade_scale_factor;
|
||||
#endif
|
||||
|
||||
color = colorClamp(color);
|
||||
|
||||
#if pp_FogCtrl == 0 || pp_TwoVolumes == 1 // LUT
|
||||
IF(cur_fog_control == 0)
|
||||
{
|
||||
color.rgb = mix(color.rgb, uniformBuffer.sp_FOG_COL_RAM.rgb, fog_mode2(gl_FragCoord.w));
|
||||
}
|
||||
#endif
|
||||
#if pp_Offset==1 && pp_BumpMap == 0 && (pp_FogCtrl == 1 || pp_TwoVolumes == 1) // Per vertex
|
||||
IF(cur_fog_control == 1)
|
||||
{
|
||||
color.rgb = mix(color.rgb, uniformBuffer.sp_FOG_COL_VERT.rgb, offset.a);
|
||||
}
|
||||
#endif
|
||||
|
||||
color *= pushConstants.trilinearAlpha;
|
||||
|
||||
#if cp_AlphaTest == 1
|
||||
color.a = 1.0;
|
||||
#endif
|
||||
|
||||
//color.rgb=vec3(gl_FragCoord.w * uniformBuffer.sp_FOG_DENSITY / 128.0);
|
||||
|
||||
#if PASS == 1
|
||||
FragColor = color;
|
||||
#elif PASS > 1
|
||||
// Discard as many pixels as possible
|
||||
switch (cur_blend_mode.y) // DST
|
||||
{
|
||||
case ONE:
|
||||
switch (cur_blend_mode.x) // SRC
|
||||
{
|
||||
case ZERO:
|
||||
discard;
|
||||
case ONE:
|
||||
case OTHER_COLOR:
|
||||
case INVERSE_OTHER_COLOR:
|
||||
if (color == vec4(0.0))
|
||||
discard;
|
||||
break;
|
||||
case SRC_ALPHA:
|
||||
if (color.a == 0.0 || color.rgb == vec3(0.0))
|
||||
discard;
|
||||
break;
|
||||
case INVERSE_SRC_ALPHA:
|
||||
if (color.a == 1.0 || color.rgb == vec3(0.0))
|
||||
discard;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case OTHER_COLOR:
|
||||
if (cur_blend_mode.x == ZERO && color == vec4(1.0))
|
||||
discard;
|
||||
break;
|
||||
case INVERSE_OTHER_COLOR:
|
||||
if (cur_blend_mode.x <= SRC_ALPHA && color == vec4(0.0))
|
||||
discard;
|
||||
break;
|
||||
case SRC_ALPHA:
|
||||
if ((cur_blend_mode.x == ZERO || cur_blend_mode.x == INVERSE_SRC_ALPHA) && color.a == 1.0)
|
||||
discard;
|
||||
break;
|
||||
case INVERSE_SRC_ALPHA:
|
||||
switch (cur_blend_mode.x) // SRC
|
||||
{
|
||||
case ZERO:
|
||||
case SRC_ALPHA:
|
||||
if (color.a == 0.0)
|
||||
discard;
|
||||
break;
|
||||
case ONE:
|
||||
case OTHER_COLOR:
|
||||
case INVERSE_OTHER_COLOR:
|
||||
if (color == vec4(0.0))
|
||||
discard;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ivec2 coords = ivec2(gl_FragCoord.xy);
|
||||
uint idx = getNextPixelIndex();
|
||||
|
||||
Pixel pixel;
|
||||
pixel.color = color;
|
||||
pixel.depth = gl_FragDepth;
|
||||
pixel.seq_num = uint(pushConstants.pp_Number);
|
||||
pixel.next = imageAtomicExchange(abufferPointerImg, coords, idx);
|
||||
PixelBuffer.pixels[idx] = pixel;
|
||||
|
||||
#endif
|
||||
}
|
||||
)";
|
||||
|
||||
// FIXME duplicate of non-oit
|
||||
static const char OITModVolVertexShaderSource[] = R"(
|
||||
#version 450
|
||||
|
||||
layout (std140, set = 0, binding = 0) uniform VertexShaderUniforms
|
||||
{
|
||||
mat4 normal_matrix;
|
||||
} uniformBuffer;
|
||||
|
||||
layout (location = 0) in vec4 in_pos;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 vpos = in_pos;
|
||||
if (vpos.z < 0.0 || vpos.z > 3.4e37)
|
||||
{
|
||||
gl_Position = vec4(0.0, 0.0, 1.0, 1.0 / vpos.z);
|
||||
return;
|
||||
}
|
||||
|
||||
vpos = uniformBuffer.normal_matrix * vpos;
|
||||
vpos.w = 1.0 / vpos.z;
|
||||
vpos.z = vpos.w;
|
||||
vpos.xy *= vpos.w;
|
||||
gl_Position = vpos;
|
||||
}
|
||||
)";
|
||||
|
||||
static const char OITModifierVolumeShader[] = R"(
|
||||
|
||||
void main()
|
||||
{
|
||||
setFragDepth();
|
||||
}
|
||||
)";
|
||||
|
||||
#define MAX_PIXELS_PER_FRAGMENT "32"
|
||||
|
||||
static const char OITFinalShaderSource[] =
|
||||
"#define MAX_PIXELS_PER_FRAGMENT " MAX_PIXELS_PER_FRAGMENT
|
||||
R"(
|
||||
#define DEPTH_SORTED %d
|
||||
|
||||
layout (input_attachment_index = 0, set = 3, binding = 0) uniform subpassInput tex;
|
||||
|
||||
layout (location = 0) out vec4 FragColor;
|
||||
|
||||
uint pixel_list[MAX_PIXELS_PER_FRAGMENT];
|
||||
|
||||
|
||||
int fillAndSortFragmentArray(ivec2 coords)
|
||||
{
|
||||
// Load fragments into a local memory array for sorting
|
||||
uint idx = imageLoad(abufferPointerImg, coords).x;
|
||||
int count = 0;
|
||||
for (; idx != EOL && count < MAX_PIXELS_PER_FRAGMENT; count++)
|
||||
{
|
||||
const Pixel p = PixelBuffer.pixels[idx];
|
||||
int j = count - 1;
|
||||
Pixel jp = PixelBuffer.pixels[pixel_list[j]];
|
||||
#if DEPTH_SORTED == 1
|
||||
while (j >= 0
|
||||
&& (jp.depth > p.depth
|
||||
|| (jp.depth == p.depth && getPolyNumber(jp) > getPolyNumber(p))))
|
||||
#else
|
||||
while (j >= 0 && getPolyNumber(jp) > getPolyNumber(p))
|
||||
#endif
|
||||
{
|
||||
pixel_list[j + 1] = pixel_list[j];
|
||||
j--;
|
||||
jp = PixelBuffer.pixels[pixel_list[j]];
|
||||
}
|
||||
pixel_list[j + 1] = idx;
|
||||
idx = p.next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Blend fragments back-to-front
|
||||
vec4 resolveAlphaBlend(ivec2 coords) {
|
||||
|
||||
// Copy and sort fragments into a local array
|
||||
int num_frag = fillAndSortFragmentArray(coords);
|
||||
|
||||
vec4 finalColor = subpassLoad(tex);
|
||||
vec4 secondaryBuffer = vec4(0.0); // Secondary accumulation buffer
|
||||
float depth = 1.0;
|
||||
|
||||
bool do_depth_test = false;
|
||||
for (int i = 0; i < num_frag; i++)
|
||||
{
|
||||
const Pixel pixel = PixelBuffer.pixels[pixel_list[i]];
|
||||
const PolyParam pp = TrPolyParam.tr_poly_params[getPolyNumber(pixel)];
|
||||
#if DEPTH_SORTED != 1
|
||||
const float frag_depth = pixel.depth;
|
||||
if (do_depth_test)
|
||||
{
|
||||
switch (getDepthFunc(pp))
|
||||
{
|
||||
case 0: // Never
|
||||
continue;
|
||||
case 1: // Less
|
||||
if (frag_depth >= depth)
|
||||
continue;
|
||||
break;
|
||||
case 2: // Equal
|
||||
if (frag_depth != depth)
|
||||
continue;
|
||||
break;
|
||||
case 3: // Less or equal
|
||||
if (frag_depth > depth)
|
||||
continue;
|
||||
break;
|
||||
case 4: // Greater
|
||||
if (frag_depth <= depth)
|
||||
continue;
|
||||
break;
|
||||
case 5: // Not equal
|
||||
if (frag_depth == depth)
|
||||
continue;
|
||||
break;
|
||||
case 6: // Greater or equal
|
||||
if (frag_depth < depth)
|
||||
continue;
|
||||
break;
|
||||
case 7: // Always
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (getDepthMask(pp))
|
||||
{
|
||||
depth = frag_depth;
|
||||
do_depth_test = true;
|
||||
}
|
||||
#endif
|
||||
bool area1 = false;
|
||||
bool shadowed = false;
|
||||
if (isShadowed(pixel))
|
||||
{
|
||||
if (isTwoVolumes(pp))
|
||||
area1 = true;
|
||||
else
|
||||
shadowed = true;
|
||||
}
|
||||
vec4 srcColor;
|
||||
if (getSrcSelect(pp, area1))
|
||||
srcColor = secondaryBuffer;
|
||||
else
|
||||
{
|
||||
srcColor = pixel.color;
|
||||
if (shadowed)
|
||||
srcColor.rgb *= uniformBuffer.shade_scale_factor;
|
||||
}
|
||||
vec4 dstColor = getDstSelect(pp, area1) ? secondaryBuffer : finalColor;
|
||||
vec4 srcCoef;
|
||||
vec4 dstCoef;
|
||||
|
||||
int srcBlend = getSrcBlendFunc(pp, area1);
|
||||
switch (srcBlend)
|
||||
{
|
||||
case ZERO:
|
||||
srcCoef = vec4(0.0);
|
||||
break;
|
||||
case ONE:
|
||||
srcCoef = vec4(1.0);
|
||||
break;
|
||||
case OTHER_COLOR:
|
||||
srcCoef = finalColor;
|
||||
break;
|
||||
case INVERSE_OTHER_COLOR:
|
||||
srcCoef = vec4(1.0) - dstColor;
|
||||
break;
|
||||
case SRC_ALPHA:
|
||||
srcCoef = vec4(srcColor.a);
|
||||
break;
|
||||
case INVERSE_SRC_ALPHA:
|
||||
srcCoef = vec4(1.0 - srcColor.a);
|
||||
break;
|
||||
case DST_ALPHA:
|
||||
srcCoef = vec4(dstColor.a);
|
||||
break;
|
||||
case INVERSE_DST_ALPHA:
|
||||
srcCoef = vec4(1.0 - dstColor.a);
|
||||
break;
|
||||
}
|
||||
int dstBlend = getDstBlendFunc(pp, area1);
|
||||
switch (dstBlend)
|
||||
{
|
||||
case ZERO:
|
||||
dstCoef = vec4(0.0);
|
||||
break;
|
||||
case ONE:
|
||||
dstCoef = vec4(1.0);
|
||||
break;
|
||||
case OTHER_COLOR:
|
||||
dstCoef = srcColor;
|
||||
break;
|
||||
case INVERSE_OTHER_COLOR:
|
||||
dstCoef = vec4(1.0) - srcColor;
|
||||
break;
|
||||
case SRC_ALPHA:
|
||||
dstCoef = vec4(srcColor.a);
|
||||
break;
|
||||
case INVERSE_SRC_ALPHA:
|
||||
dstCoef = vec4(1.0 - srcColor.a);
|
||||
break;
|
||||
case DST_ALPHA:
|
||||
dstCoef = vec4(dstColor.a);
|
||||
break;
|
||||
case INVERSE_DST_ALPHA:
|
||||
dstCoef = vec4(1.0 - dstColor.a);
|
||||
break;
|
||||
}
|
||||
const vec4 result = clamp(dstColor * dstCoef + srcColor * srcCoef, 0.0, 1.0);
|
||||
if (getDstSelect(pp, area1))
|
||||
secondaryBuffer = result;
|
||||
else
|
||||
finalColor = result;
|
||||
}
|
||||
|
||||
return finalColor;
|
||||
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
ivec2 coords = ivec2(gl_FragCoord.xy);
|
||||
// Compute and output final color for the frame buffer
|
||||
// Visualize the number of layers in use
|
||||
//FragColor = vec4(float(fillAndSortFragmentArray(coords)) / MAX_PIXELS_PER_FRAGMENT * 4, 0, 0, 1);
|
||||
FragColor = resolveAlphaBlend(coords);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char OITClearShaderSource[] = R"(
|
||||
void main(void)
|
||||
{
|
||||
ivec2 coords = ivec2(gl_FragCoord.xy);
|
||||
|
||||
// Reset pointers
|
||||
imageStore(abufferPointerImg, coords, uvec4(EOL));
|
||||
}
|
||||
)";
|
||||
|
||||
static const char OITTranslucentModvolShaderSource[] =
|
||||
"#define MAX_PIXELS_PER_FRAGMENT " MAX_PIXELS_PER_FRAGMENT
|
||||
R"(
|
||||
#define MV_MODE %d
|
||||
|
||||
// Must match ModifierVolumeMode enum values
|
||||
#define MV_XOR 0
|
||||
#define MV_OR 1
|
||||
#define MV_INCLUSION 2
|
||||
#define MV_EXCLUSION 3
|
||||
|
||||
void main()
|
||||
{
|
||||
#if MV_MODE == MV_XOR || MV_MODE == MV_OR
|
||||
setFragDepth();
|
||||
#endif
|
||||
ivec2 coords = ivec2(gl_FragCoord.xy);
|
||||
|
||||
uint idx = imageLoad(abufferPointerImg, coords).x;
|
||||
int list_len = 0;
|
||||
while (idx != EOL && list_len < MAX_PIXELS_PER_FRAGMENT)
|
||||
{
|
||||
const Pixel pixel = PixelBuffer.pixels[idx];
|
||||
const PolyParam pp = TrPolyParam.tr_poly_params[getPolyNumber(pixel)];
|
||||
if (getShadowEnable(pp))
|
||||
{
|
||||
#if MV_MODE == MV_XOR
|
||||
if (gl_FragDepth >= pixel.depth)
|
||||
atomicXor(PixelBuffer.pixels[idx].seq_num, SHADOW_STENCIL);
|
||||
#elif MV_MODE == MV_OR
|
||||
if (gl_FragDepth >= pixel.depth)
|
||||
atomicOr(PixelBuffer.pixels[idx].seq_num, SHADOW_STENCIL);
|
||||
#elif MV_MODE == MV_INCLUSION
|
||||
uint prev_val = atomicAnd(PixelBuffer.pixels[idx].seq_num, ~(SHADOW_STENCIL));
|
||||
if ((prev_val & (SHADOW_STENCIL|SHADOW_ACC)) == SHADOW_STENCIL)
|
||||
PixelBuffer.pixels[idx].seq_num = bitfieldInsert(pixel.seq_num, 1u, 31, 1);
|
||||
#elif MV_MODE == MV_EXCLUSION
|
||||
uint prev_val = atomicAnd(PixelBuffer.pixels[idx].seq_num, ~(SHADOW_STENCIL|SHADOW_ACC));
|
||||
if ((prev_val & (SHADOW_STENCIL|SHADOW_ACC)) == SHADOW_ACC)
|
||||
PixelBuffer.pixels[idx].seq_num = bitfieldInsert(pixel.seq_num, 1u, 31, 1);
|
||||
#endif
|
||||
}
|
||||
idx = pixel.next;
|
||||
list_len++;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char OITFinalVertexShaderSource[] = R"(
|
||||
#version 430
|
||||
|
||||
layout (location = 0) in vec3 in_pos;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(in_pos, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
vk::UniqueShaderModule OITShaderManager::compileShader(const VertexShaderParams& params)
|
||||
{
|
||||
char buf[sizeof(OITVertexShaderSource) * 2];
|
||||
|
||||
sprintf(buf, OITVertexShaderSource, (int)params.gouraud);
|
||||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eVertex, buf);
|
||||
}
|
||||
|
||||
vk::UniqueShaderModule OITShaderManager::compileShader(const FragmentShaderParams& params)
|
||||
{
|
||||
char buf[(sizeof(OITShaderHeader) + sizeof(OITFragmentShaderSource)) * 2];
|
||||
|
||||
strcpy(buf, OITShaderHeader);
|
||||
sprintf(buf + strlen(buf), OITFragmentShaderSource, (int)params.alphaTest, (int)params.insideClipTest, (int)params.useAlpha,
|
||||
(int)params.texture, (int)params.ignoreTexAlpha, params.shaderInstr, (int)params.offset, params.fog,
|
||||
(int)params.twoVolume, params.depthFunc, (int)params.gouraud, (int)params.bumpmap, (int)params.clamping, params.pass);
|
||||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, buf);
|
||||
}
|
||||
|
||||
vk::UniqueShaderModule OITShaderManager::compileFinalShader(bool autosort)
|
||||
{
|
||||
char buf[(sizeof(OITShaderHeader) + sizeof(OITFinalShaderSource)) * 2];
|
||||
|
||||
strcpy(buf, OITShaderHeader);
|
||||
sprintf(buf + strlen(buf), OITFinalShaderSource, (int)autosort);
|
||||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, buf);
|
||||
}
|
||||
vk::UniqueShaderModule OITShaderManager::compileFinalVertexShader()
|
||||
{
|
||||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eVertex, OITFinalVertexShaderSource);
|
||||
}
|
||||
vk::UniqueShaderModule OITShaderManager::compileClearShader()
|
||||
{
|
||||
std::string source = OITShaderHeader;
|
||||
source += OITClearShaderSource;
|
||||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, source);
|
||||
}
|
||||
vk::UniqueShaderModule OITShaderManager::compileModVolVertexShader()
|
||||
{
|
||||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eVertex, OITModVolVertexShaderSource);
|
||||
}
|
||||
vk::UniqueShaderModule OITShaderManager::compileModVolFragmentShader()
|
||||
{
|
||||
std::string source = OITShaderHeader;
|
||||
source += OITModifierVolumeShader;
|
||||
return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, source);
|
||||
}
|
||||
void OITShaderManager::compileTrModVolFragmentShader(ModVolMode mode)
|
||||
{
|
||||
if (trModVolShaders.empty())
|
||||
trModVolShaders.resize((size_t)ModVolMode::Final);
|
||||
char buf[(sizeof(OITShaderHeader) + sizeof(OITTranslucentModvolShaderSource)) * 2];
|
||||
|
||||
strcpy(buf, OITShaderHeader);
|
||||
sprintf(buf + strlen(buf), OITTranslucentModvolShaderSource, (int)mode);
|
||||
trModVolShaders[(size_t)mode] = ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, buf);
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
Created on: Oct 27, 2019
|
||||
|
||||
Copyright 2019 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "vulkan.h"
|
||||
#include "utils.h"
|
||||
|
||||
class OITShaderManager
|
||||
{
|
||||
public:
|
||||
struct VertexShaderParams
|
||||
{
|
||||
bool gouraud;
|
||||
|
||||
u32 hash() { return (u32)gouraud; }
|
||||
};
|
||||
|
||||
// alpha test, clip test, use alpha, texture, ignore alpha, shader instr, offset, fog, gouraud, bump, clamp
|
||||
struct FragmentShaderParams
|
||||
{
|
||||
bool alphaTest;
|
||||
bool insideClipTest;
|
||||
bool useAlpha;
|
||||
bool texture;
|
||||
bool ignoreTexAlpha;
|
||||
int shaderInstr;
|
||||
bool offset;
|
||||
int fog;
|
||||
bool gouraud;
|
||||
bool bumpmap;
|
||||
bool clamping;
|
||||
bool twoVolume;
|
||||
int depthFunc;
|
||||
int pass;
|
||||
|
||||
u32 hash()
|
||||
{
|
||||
return ((u32)alphaTest) | ((u32)insideClipTest << 1) | ((u32)useAlpha << 2)
|
||||
| ((u32)texture << 3) | ((u32)ignoreTexAlpha << 4) | (shaderInstr << 5)
|
||||
| ((u32)offset << 7) | ((u32)fog << 8) | ((u32)gouraud << 10)
|
||||
| ((u32)bumpmap << 11) | ((u32)clamping << 12) | ((u32)twoVolume << 13)
|
||||
| (depthFunc << 14) | (pass << 17);
|
||||
}
|
||||
};
|
||||
|
||||
vk::ShaderModule GetVertexShader(const VertexShaderParams& params) { return getShader(vertexShaders, params); }
|
||||
vk::ShaderModule GetFragmentShader(const FragmentShaderParams& params) { return getShader(fragmentShaders, params); }
|
||||
vk::ShaderModule GetModVolVertexShader()
|
||||
{
|
||||
if (!modVolVertexShader)
|
||||
modVolVertexShader = compileModVolVertexShader();
|
||||
return *modVolVertexShader;
|
||||
}
|
||||
vk::ShaderModule GetModVolShader()
|
||||
{
|
||||
if (!modVolShader)
|
||||
modVolShader = compileModVolFragmentShader();
|
||||
return *modVolShader;
|
||||
}
|
||||
vk::ShaderModule GetTrModVolShader(ModVolMode mode)
|
||||
{
|
||||
if (trModVolShaders.empty() || !trModVolShaders[(size_t)mode])
|
||||
compileTrModVolFragmentShader(mode);
|
||||
return *trModVolShaders[(size_t)mode];
|
||||
}
|
||||
// vk::ShaderModule GetQuadVertexShader()
|
||||
// {
|
||||
// if (!quadVertexShader)
|
||||
// quadVertexShader = compileQuadVertexShader();
|
||||
// return *quadVertexShader;
|
||||
// }
|
||||
// vk::ShaderModule GetQuadFragmentShader()
|
||||
// {
|
||||
// if (!quadFragmentShader)
|
||||
// quadFragmentShader = compileQuadFragmentShader();
|
||||
// return *quadFragmentShader;
|
||||
// }
|
||||
// vk::ShaderModule GetOSDVertexShader()
|
||||
// {
|
||||
// if (!osdVertexShader)
|
||||
// osdVertexShader = compileOSDVertexShader();
|
||||
// return *osdVertexShader;
|
||||
// }
|
||||
// vk::ShaderModule GetOSDFragmentShader()
|
||||
// {
|
||||
// if (!osdFragmentShader)
|
||||
// osdFragmentShader = compileOSDFragmentShader();
|
||||
// return *osdFragmentShader;
|
||||
// }
|
||||
|
||||
vk::ShaderModule GetFinalShader(bool autosort)
|
||||
{
|
||||
if (autosort)
|
||||
{
|
||||
if (!finalAutosortShader)
|
||||
finalAutosortShader = compileFinalShader(true);
|
||||
return *finalAutosortShader;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!finalSortedShader)
|
||||
finalSortedShader = compileFinalShader(false);
|
||||
return *finalSortedShader;
|
||||
}
|
||||
}
|
||||
vk::ShaderModule GetFinalVertexShader()
|
||||
{
|
||||
if (!finalVertexShader)
|
||||
finalVertexShader = compileFinalVertexShader();
|
||||
return *finalVertexShader;
|
||||
}
|
||||
vk::ShaderModule GetClearShader()
|
||||
{
|
||||
if (!clearShader)
|
||||
clearShader = compileClearShader();
|
||||
return *clearShader;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
vk::ShaderModule getShader(std::map<u32, vk::UniqueShaderModule>& map, T params)
|
||||
{
|
||||
auto it = map.find(params.hash());
|
||||
if (it != map.end())
|
||||
return it->second.get();
|
||||
map[params.hash()] = compileShader(params);
|
||||
return map[params.hash()].get();
|
||||
}
|
||||
vk::UniqueShaderModule compileShader(const VertexShaderParams& params);
|
||||
vk::UniqueShaderModule compileShader(const FragmentShaderParams& params);
|
||||
vk::UniqueShaderModule compileModVolVertexShader();
|
||||
vk::UniqueShaderModule compileModVolFragmentShader();
|
||||
void compileTrModVolFragmentShader(ModVolMode mode);
|
||||
// vk::UniqueShaderModule compileQuadVertexShader();
|
||||
// vk::UniqueShaderModule compileQuadFragmentShader();
|
||||
// vk::UniqueShaderModule compileOSDVertexShader();
|
||||
// vk::UniqueShaderModule compileOSDFragmentShader();
|
||||
vk::UniqueShaderModule compileFinalShader(bool autosort);
|
||||
vk::UniqueShaderModule compileFinalVertexShader();
|
||||
vk::UniqueShaderModule compileClearShader();
|
||||
|
||||
std::map<u32, vk::UniqueShaderModule> vertexShaders;
|
||||
std::map<u32, vk::UniqueShaderModule> fragmentShaders;
|
||||
vk::UniqueShaderModule modVolVertexShader;
|
||||
vk::UniqueShaderModule modVolShader;
|
||||
std::vector<vk::UniqueShaderModule> trModVolShaders;
|
||||
// vk::UniqueShaderModule quadVertexShader;
|
||||
// vk::UniqueShaderModule quadFragmentShader;
|
||||
// vk::UniqueShaderModule osdVertexShader;
|
||||
// vk::UniqueShaderModule osdFragmentShader;
|
||||
|
||||
vk::UniqueShaderModule finalVertexShader;
|
||||
vk::UniqueShaderModule finalAutosortShader;
|
||||
vk::UniqueShaderModule finalSortedShader;
|
||||
vk::UniqueShaderModule clearShader;
|
||||
};
|
||||
|
Loading…
Reference in New Issue