renderer_vulkan: Add very basic pipeline and shader cache
This commit is contained in:
@ -82,6 +82,8 @@ add_library(video_core STATIC
|
|||||||
renderer_vulkan/vk_rasterizer.cpp
|
renderer_vulkan/vk_rasterizer.cpp
|
||||||
renderer_vulkan/vk_rasterizer.h
|
renderer_vulkan/vk_rasterizer.h
|
||||||
renderer_vulkan/vk_shader_state.h
|
renderer_vulkan/vk_shader_state.h
|
||||||
|
renderer_vulkan/vk_shader_gen.cpp
|
||||||
|
renderer_vulkan/vk_shader_gen.h
|
||||||
renderer_vulkan/vk_state.cpp
|
renderer_vulkan/vk_state.cpp
|
||||||
renderer_vulkan/vk_state.h
|
renderer_vulkan/vk_state.h
|
||||||
renderer_vulkan/vk_surface_params.cpp
|
renderer_vulkan/vk_surface_params.cpp
|
||||||
|
@ -29,24 +29,12 @@
|
|||||||
#include "core/tracer/recorder.h"
|
#include "core/tracer/recorder.h"
|
||||||
#include "video_core/debug_utils/debug_utils.h"
|
#include "video_core/debug_utils/debug_utils.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
#include "video_core/renderer_opengl/gl_state.h"
|
#include "video_core/renderer_vulkan/vk_state.h"
|
||||||
#include "video_core/renderer_opengl/gl_vars.h"
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
#include "video_core/renderer_opengl/post_processing_opengl.h"
|
|
||||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
|
|
||||||
// to wait on available presentation frames. There doesn't seem to be much of a downside to a larger
|
|
||||||
// number but 9 swap textures at 60FPS presentation allows for 800% speed so thats probably fine
|
|
||||||
#ifdef ANDROID
|
|
||||||
// Reduce the size of swap_chain, since the UI only allows upto 200% speed.
|
|
||||||
constexpr std::size_t SWAP_CHAIN_SIZE = 6;
|
|
||||||
#else
|
|
||||||
constexpr std::size_t SWAP_CHAIN_SIZE = 9;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class OGLTextureMailboxException : public std::runtime_error {
|
class OGLTextureMailboxException : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
using std::runtime_error::runtime_error;
|
using std::runtime_error::runtime_error;
|
||||||
@ -371,13 +359,13 @@ RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
|
|||||||
frame_dumper.mailbox = std::make_unique<OGLVideoDumpingMailbox>();
|
frame_dumper.mailbox = std::make_unique<OGLVideoDumpingMailbox>();
|
||||||
}
|
}
|
||||||
|
|
||||||
RendererOpenGL::~RendererOpenGL() = default;
|
RendererVulkan::~RendererVulkan() = default;
|
||||||
|
|
||||||
MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
|
MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
|
||||||
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
|
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
|
||||||
|
|
||||||
/// Swap buffers (render frame)
|
/// Swap buffers (render frame)
|
||||||
void RendererOpenGL::SwapBuffers() {
|
void RendererVulkan::SwapBuffers() {
|
||||||
// Maintain the rasterizer's state as a priority
|
// Maintain the rasterizer's state as a priority
|
||||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||||
state.Apply();
|
state.Apply();
|
||||||
@ -415,7 +403,7 @@ void RendererOpenGL::SwapBuffers() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::RenderScreenshot() {
|
void RendererVulkan::RenderScreenshot() {
|
||||||
if (VideoCore::g_renderer_screenshot_requested) {
|
if (VideoCore::g_renderer_screenshot_requested) {
|
||||||
// Draw this frame to the screenshot framebuffer
|
// Draw this frame to the screenshot framebuffer
|
||||||
screenshot_framebuffer.Create();
|
screenshot_framebuffer.Create();
|
||||||
@ -449,7 +437,7 @@ void RendererOpenGL::RenderScreenshot() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::PrepareRendertarget() {
|
void RendererVulkan::PrepareRendertarget() {
|
||||||
for (int i : {0, 1, 2}) {
|
for (int i : {0, 1, 2}) {
|
||||||
int fb_id = i == 2 ? 1 : 0;
|
int fb_id = i == 2 ? 1 : 0;
|
||||||
const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id];
|
const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id];
|
||||||
@ -486,7 +474,7 @@ void RendererOpenGL::PrepareRendertarget() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::RenderToMailbox(const Layout::FramebufferLayout& layout,
|
void RendererVulkan::RenderToMailbox(const Layout::FramebufferLayout& layout,
|
||||||
std::unique_ptr<Frontend::TextureMailbox>& mailbox,
|
std::unique_ptr<Frontend::TextureMailbox>& mailbox,
|
||||||
bool flipped) {
|
bool flipped) {
|
||||||
|
|
||||||
@ -540,7 +528,7 @@ void RendererOpenGL::RenderToMailbox(const Layout::FramebufferLayout& layout,
|
|||||||
/**
|
/**
|
||||||
* Loads framebuffer from emulated memory into the active OpenGL texture.
|
* Loads framebuffer from emulated memory into the active OpenGL texture.
|
||||||
*/
|
*/
|
||||||
void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer,
|
void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer,
|
||||||
ScreenInfo& screen_info, bool right_eye) {
|
ScreenInfo& screen_info, bool right_eye) {
|
||||||
|
|
||||||
if (framebuffer.address_right1 == 0 || framebuffer.address_right2 == 0)
|
if (framebuffer.address_right1 == 0 || framebuffer.address_right2 == 0)
|
||||||
@ -601,7 +589,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
|
|||||||
* Fills active OpenGL texture with the given RGB color. Since the color is solid, the texture can
|
* Fills active OpenGL texture with the given RGB color. Since the color is solid, the texture can
|
||||||
* be 1x1 but will stretch across whatever it's rendered on.
|
* be 1x1 but will stretch across whatever it's rendered on.
|
||||||
*/
|
*/
|
||||||
void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
|
void RendererVulkan::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b,
|
||||||
const TextureInfo& texture) {
|
const TextureInfo& texture) {
|
||||||
state.texture_units[0].texture_2d = texture.resource.handle;
|
state.texture_units[0].texture_2d = texture.resource.handle;
|
||||||
state.Apply();
|
state.Apply();
|
||||||
@ -619,7 +607,7 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
|
|||||||
/**
|
/**
|
||||||
* Initializes the OpenGL state and creates persistent objects.
|
* Initializes the OpenGL state and creates persistent objects.
|
||||||
*/
|
*/
|
||||||
void RendererOpenGL::InitOpenGLObjects() {
|
void RendererVulkan::InitOpenGLObjects() {
|
||||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||||
0.0f);
|
0.0f);
|
||||||
|
|
||||||
@ -672,7 +660,7 @@ void RendererOpenGL::InitOpenGLObjects() {
|
|||||||
state.Apply();
|
state.Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::ReloadSampler() {
|
void RendererVulkan::ReloadSampler() {
|
||||||
glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MIN_FILTER,
|
glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MIN_FILTER,
|
||||||
Settings::values.filter_mode ? GL_LINEAR : GL_NEAREST);
|
Settings::values.filter_mode ? GL_LINEAR : GL_NEAREST);
|
||||||
glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MAG_FILTER,
|
glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MAG_FILTER,
|
||||||
@ -681,7 +669,7 @@ void RendererOpenGL::ReloadSampler() {
|
|||||||
glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::ReloadShader() {
|
void RendererVulkan::ReloadShader() {
|
||||||
// Link shaders and get variable locations
|
// Link shaders and get variable locations
|
||||||
std::string shader_data;
|
std::string shader_data;
|
||||||
if (GLES) {
|
if (GLES) {
|
||||||
@ -754,7 +742,7 @@ void RendererOpenGL::ReloadShader() {
|
|||||||
attrib_tex_coord = glGetAttribLocation(shader.handle, "vert_tex_coord");
|
attrib_tex_coord = glGetAttribLocation(shader.handle, "vert_tex_coord");
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||||
const GPU::Regs::FramebufferConfig& framebuffer) {
|
const GPU::Regs::FramebufferConfig& framebuffer) {
|
||||||
GPU::Regs::PixelFormat format = framebuffer.color_format;
|
GPU::Regs::PixelFormat format = framebuffer.color_format;
|
||||||
GLint internal_format;
|
GLint internal_format;
|
||||||
@ -819,7 +807,7 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
|||||||
* Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
|
* Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
|
||||||
* rotation.
|
* rotation.
|
||||||
*/
|
*/
|
||||||
void RendererOpenGL::DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y,
|
void RendererVulkan::DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y,
|
||||||
float w, float h) {
|
float w, float h) {
|
||||||
const auto& texcoords = screen_info.display_texcoords;
|
const auto& texcoords = screen_info.display_texcoords;
|
||||||
|
|
||||||
@ -851,7 +839,7 @@ void RendererOpenGL::DrawSingleScreenRotated(const ScreenInfo& screen_info, floa
|
|||||||
state.Apply();
|
state.Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w,
|
void RendererVulkan::DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w,
|
||||||
float h) {
|
float h) {
|
||||||
const auto& texcoords = screen_info.display_texcoords;
|
const auto& texcoords = screen_info.display_texcoords;
|
||||||
|
|
||||||
@ -884,7 +872,7 @@ void RendererOpenGL::DrawSingleScreen(const ScreenInfo& screen_info, float x, fl
|
|||||||
* Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
|
* Draws a single texture to the emulator window, rotating the texture to correct for the 3DS's LCD
|
||||||
* rotation.
|
* rotation.
|
||||||
*/
|
*/
|
||||||
void RendererOpenGL::DrawSingleScreenStereoRotated(const ScreenInfo& screen_info_l,
|
void RendererVulkan::DrawSingleScreenStereoRotated(const ScreenInfo& screen_info_l,
|
||||||
const ScreenInfo& screen_info_r, float x,
|
const ScreenInfo& screen_info_r, float x,
|
||||||
float y, float w, float h) {
|
float y, float w, float h) {
|
||||||
const auto& texcoords = screen_info_l.display_texcoords;
|
const auto& texcoords = screen_info_l.display_texcoords;
|
||||||
@ -919,7 +907,7 @@ void RendererOpenGL::DrawSingleScreenStereoRotated(const ScreenInfo& screen_info
|
|||||||
state.Apply();
|
state.Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l,
|
void RendererVulkan::DrawSingleScreenStereo(const ScreenInfo& screen_info_l,
|
||||||
const ScreenInfo& screen_info_r, float x, float y,
|
const ScreenInfo& screen_info_r, float x, float y,
|
||||||
float w, float h) {
|
float w, float h) {
|
||||||
const auto& texcoords = screen_info_l.display_texcoords;
|
const auto& texcoords = screen_info_l.display_texcoords;
|
||||||
@ -957,7 +945,7 @@ void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l,
|
|||||||
/**
|
/**
|
||||||
* Draws the emulated screens to the emulator window.
|
* Draws the emulated screens to the emulator window.
|
||||||
*/
|
*/
|
||||||
void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool flipped) {
|
void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool flipped) {
|
||||||
if (VideoCore::g_renderer_bg_color_update_requested.exchange(false)) {
|
if (VideoCore::g_renderer_bg_color_update_requested.exchange(false)) {
|
||||||
// Update background color before drawing
|
// Update background color before drawing
|
||||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||||
@ -1123,7 +1111,7 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::TryPresent(int timeout_ms) {
|
void RendererVulkan::TryPresent(int timeout_ms) {
|
||||||
const auto& layout = render_window.GetFramebufferLayout();
|
const auto& layout = render_window.GetFramebufferLayout();
|
||||||
auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms);
|
auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms);
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
@ -1164,9 +1152,9 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the framerate
|
/// Updates the framerate
|
||||||
void RendererOpenGL::UpdateFramerate() {}
|
void RendererVulkan::UpdateFramerate() {}
|
||||||
|
|
||||||
void RendererOpenGL::PrepareVideoDumping() {
|
void RendererVulkan::PrepareVideoDumping() {
|
||||||
auto* mailbox = static_cast<OGLVideoDumpingMailbox*>(frame_dumper.mailbox.get());
|
auto* mailbox = static_cast<OGLVideoDumpingMailbox*>(frame_dumper.mailbox.get());
|
||||||
{
|
{
|
||||||
std::unique_lock lock(mailbox->swap_chain_lock);
|
std::unique_lock lock(mailbox->swap_chain_lock);
|
||||||
@ -1175,7 +1163,7 @@ void RendererOpenGL::PrepareVideoDumping() {
|
|||||||
frame_dumper.StartDumping();
|
frame_dumper.StartDumping();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::CleanupVideoDumping() {
|
void RendererVulkan::CleanupVideoDumping() {
|
||||||
frame_dumper.StopDumping();
|
frame_dumper.StopDumping();
|
||||||
auto* mailbox = static_cast<OGLVideoDumpingMailbox*>(frame_dumper.mailbox.get());
|
auto* mailbox = static_cast<OGLVideoDumpingMailbox*>(frame_dumper.mailbox.get());
|
||||||
{
|
{
|
||||||
@ -1240,7 +1228,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the renderer
|
/// Initialize the renderer
|
||||||
VideoCore::ResultStatus RendererOpenGL::Init() {
|
VideoCore::ResultStatus RendererVulkan::Init() {
|
||||||
#ifndef ANDROID
|
#ifndef ANDROID
|
||||||
if (!gladLoadGL()) {
|
if (!gladLoadGL()) {
|
||||||
return VideoCore::ResultStatus::ErrorBelowGL33;
|
return VideoCore::ResultStatus::ErrorBelowGL33;
|
||||||
@ -1284,6 +1272,6 @@ VideoCore::ResultStatus RendererOpenGL::Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shutdown the renderer
|
/// Shutdown the renderer
|
||||||
void RendererOpenGL::ShutDown() {}
|
void RendererVulkan::ShutDown() {}
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
@ -62,8 +62,7 @@ private:
|
|||||||
void RenderScreenshot();
|
void RenderScreenshot();
|
||||||
void RenderToMailbox(const Layout::FramebufferLayout& layout,
|
void RenderToMailbox(const Layout::FramebufferLayout& layout,
|
||||||
std::unique_ptr<Frontend::TextureMailbox>& mailbox, bool flipped);
|
std::unique_ptr<Frontend::TextureMailbox>& mailbox, bool flipped);
|
||||||
void ConfigureFramebufferTexture(TextureInfo& texture,
|
void ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::Regs::FramebufferConfig& framebuffer);
|
||||||
const GPU::Regs::FramebufferConfig& framebuffer);
|
|
||||||
void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped);
|
void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped);
|
||||||
void DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
void DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
||||||
void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
||||||
@ -78,7 +77,7 @@ private:
|
|||||||
void LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer,
|
void LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer,
|
||||||
ScreenInfo& screen_info, bool right_eye);
|
ScreenInfo& screen_info, bool right_eye);
|
||||||
// Fills active OpenGL texture with the given RGB color.
|
// Fills active OpenGL texture with the given RGB color.
|
||||||
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture);
|
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const ScreenInfo& screen);
|
||||||
|
|
||||||
VulkanState state;
|
VulkanState state;
|
||||||
|
|
||||||
|
@ -113,55 +113,67 @@ bool VKInstance::CreateDevice(vk::SurfaceKHR surface, bool validation_enabled) {
|
|||||||
|
|
||||||
bool VKInstance::FindFeatures()
|
bool VKInstance::FindFeatures()
|
||||||
{
|
{
|
||||||
auto available_features = physical_device.getFeatures();
|
auto available = physical_device.getFeatures();
|
||||||
|
|
||||||
// Not having geometry shaders or wide lines will cause issues with rendering.
|
// Not having geometry shaders or wide lines will cause issues with rendering.
|
||||||
if (!available_features.geometryShader && !available_features.wideLines) {
|
if (!available.geometryShader && !available.wideLines) {
|
||||||
LOG_WARNING(Render_Vulkan, "Geometry shaders not availabe! Rendering will be limited");
|
LOG_WARNING(Render_Vulkan, "Geometry shaders not availabe! Rendering will be limited");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable some common features other emulators like Dolphin use
|
// Enable some common features other emulators like Dolphin use
|
||||||
device_features.dualSrcBlend = available_features.dualSrcBlend;
|
vk_features.dualSrcBlend = available.dualSrcBlend;
|
||||||
device_features.geometryShader = available_features.geometryShader;
|
vk_features.geometryShader = available.geometryShader;
|
||||||
device_features.samplerAnisotropy = available_features.samplerAnisotropy;
|
vk_features.samplerAnisotropy = available.samplerAnisotropy;
|
||||||
device_features.logicOp = available_features.logicOp;
|
vk_features.logicOp = available.logicOp;
|
||||||
device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
|
vk_features.fragmentStoresAndAtomics = available.fragmentStoresAndAtomics;
|
||||||
device_features.sampleRateShading = available_features.sampleRateShading;
|
vk_features.sampleRateShading = available.sampleRateShading;
|
||||||
device_features.largePoints = available_features.largePoints;
|
vk_features.largePoints = available.largePoints;
|
||||||
device_features.shaderStorageImageMultisample = available_features.shaderStorageImageMultisample;
|
vk_features.shaderStorageImageMultisample = available.shaderStorageImageMultisample;
|
||||||
device_features.occlusionQueryPrecise = available_features.occlusionQueryPrecise;
|
vk_features.occlusionQueryPrecise = available.occlusionQueryPrecise;
|
||||||
device_features.shaderClipDistance = available_features.shaderClipDistance;
|
vk_features.shaderClipDistance = available.shaderClipDistance;
|
||||||
device_features.depthClamp = available_features.depthClamp;
|
vk_features.depthClamp = available.depthClamp;
|
||||||
device_features.textureCompressionBC = available_features.textureCompressionBC;
|
vk_features.textureCompressionBC = available.textureCompressionBC;
|
||||||
|
|
||||||
// Enable timeline semaphore support
|
// Enable newer Vulkan features
|
||||||
new_features.timelineSemaphore = true;
|
vk12_features.timelineSemaphore = true;
|
||||||
|
vk13_features.dynamicRendering = true;
|
||||||
|
dynamic_state.extendedDynamicState = true;
|
||||||
|
dynamic_state2.extendedDynamicState2 = true;
|
||||||
|
dynamic_state2.extendedDynamicState2LogicOp = true;
|
||||||
|
dynamic_state2.extendedDynamicState2PatchControlPoints = true;
|
||||||
|
|
||||||
|
// Include features in device creation
|
||||||
|
features.setFeatures(vk_features);
|
||||||
|
features.setPNext(&vk12_features);
|
||||||
|
vk12_features.setPNext(&vk13_features);
|
||||||
|
vk13_features.setPNext(&dynamic_state);
|
||||||
|
dynamic_state.setPNext(&dynamic_state2);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VKInstance::FindExtensions()
|
bool VKInstance::FindExtensions()
|
||||||
{
|
{
|
||||||
auto extensions = physical_device.enumerateDeviceExtensionProperties();
|
auto available = physical_device.enumerateDeviceExtensionProperties();
|
||||||
if (extensions.empty()) {
|
if (available.empty()) {
|
||||||
LOG_CRITICAL(Render_Vulkan, "No extensions supported by device.");
|
LOG_CRITICAL(Render_Vulkan, "No extensions supported by device.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// List available device extensions
|
// List available device extensions
|
||||||
for (const auto& prop : extensions) {
|
for (const auto& prop : available) {
|
||||||
LOG_INFO(Render_Vulkan, "Vulkan extension: {}", prop.extensionName);
|
LOG_INFO(Render_Vulkan, "Vulkan extension: {}", prop.extensionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper lambda for adding extensions
|
// Helper lambda for adding extensions
|
||||||
auto AddExtension = [&](const char* name, bool required) {
|
auto AddExtension = [&](const char* name, bool required) {
|
||||||
auto result = std::find_if(extensions.begin(), extensions.end(), [&](const auto& prop) {
|
auto result = std::find_if(available.begin(), available.end(), [&](const auto& prop) {
|
||||||
return !std::strcmp(name, prop.extensionName);
|
return !std::strcmp(name, prop.extensionName);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result != extensions.end()) {
|
if (result != available.end()) {
|
||||||
LOG_INFO(Render_Vulkan, "Enabling extension: {}", name);
|
LOG_INFO(Render_Vulkan, "Enabling extension: {}", name);
|
||||||
device_extensions.push_back(name);
|
extensions.push_back(name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,13 +184,14 @@ bool VKInstance::FindExtensions()
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The swapchain extension is required
|
// Add required extensions
|
||||||
if (!AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true)) {
|
if (!AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true) ||
|
||||||
|
!AddExtension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, true) ||
|
||||||
|
!AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, true) ||
|
||||||
|
!AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME, true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add more extensions in the future...
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,10 +52,16 @@ public:
|
|||||||
vk::UniqueDevice device;
|
vk::UniqueDevice device;
|
||||||
|
|
||||||
// Extensions and features
|
// Extensions and features
|
||||||
std::vector<const char*> device_extensions;
|
std::vector<const char*> extensions;
|
||||||
vk::PhysicalDeviceFeatures device_features{};
|
vk::PhysicalDeviceFeatures2 features{};
|
||||||
vk::PhysicalDeviceVulkan12Features new_features{};
|
|
||||||
vk::PhysicalDeviceLimits device_limits;
|
vk::PhysicalDeviceLimits device_limits;
|
||||||
|
|
||||||
|
// Features per vulkan version
|
||||||
|
vk::PhysicalDeviceFeatures vk_features{};
|
||||||
|
vk::PhysicalDeviceVulkan12Features vk12_features{};
|
||||||
|
vk::PhysicalDeviceVulkan13Features vk13_features{};
|
||||||
|
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state{};
|
||||||
|
vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT dynamic_state2{};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::unique_ptr<VKInstance> g_vk_instace;
|
extern std::unique_ptr<VKInstance> g_vk_instace;
|
||||||
|
@ -314,13 +314,6 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
|||||||
uniform_block_data.dirty = true;
|
uniform_block_data.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool need_duplicate_texture = false;
|
|
||||||
auto CheckBarrier = [&need_duplicate_texture, &color_surface](VKTexture* handle) {
|
|
||||||
if (color_surface && &color_surface->texture == handle) {
|
|
||||||
need_duplicate_texture = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sync and bind the texture surfaces
|
// Sync and bind the texture surfaces
|
||||||
const auto pica_textures = regs.texturing.GetTextures();
|
const auto pica_textures = regs.texturing.GetTextures();
|
||||||
for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
|
for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
|
||||||
@ -367,11 +360,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
|||||||
vk::Extent2D(draw_rect.GetHeight(), draw_rect.GetHeight()));
|
vk::Extent2D(draw_rect.GetHeight(), draw_rect.GetHeight()));
|
||||||
state.SetScissor(scissor);
|
state.SetScissor(scissor);
|
||||||
|
|
||||||
// Draw the vertex batch
|
// Apply pending state
|
||||||
bool succeeded = true;
|
|
||||||
shader_program_manager->UseTrivialVertexShader();
|
|
||||||
shader_program_manager->UseTrivialGeometryShader();
|
|
||||||
shader_program_manager->ApplyTo(state);
|
|
||||||
state.Apply();
|
state.Apply();
|
||||||
|
|
||||||
std::size_t max_vertices = 3 * (VERTEX_BUFFER_SIZE / (3 * sizeof(HardwareVertex)));
|
std::size_t max_vertices = 3 * (VERTEX_BUFFER_SIZE / (3 * sizeof(HardwareVertex)));
|
||||||
@ -431,7 +420,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
|||||||
depth_surface);
|
depth_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
return succeeded;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::NotifyPicaRegisterChanged(u32 id) {
|
void RasterizerVulkan::NotifyPicaRegisterChanged(u32 id) {
|
||||||
@ -1141,11 +1130,11 @@ bool RasterizerVulkan::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
|
|||||||
|
|
||||||
|
|
||||||
void RasterizerVulkan::SetShader() {
|
void RasterizerVulkan::SetShader() {
|
||||||
shader_program_manager->UseFragmentShader(Pica::g_state.regs);
|
state.SetFragmentShader(Pica::g_state.regs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::SyncClipEnabled() {
|
void RasterizerVulkan::SyncClipEnabled() {
|
||||||
state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0;
|
//state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::SyncClipCoef() {
|
void RasterizerVulkan::SyncClipCoef() {
|
||||||
|
@ -31,7 +31,6 @@ class EmuWindow;
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
class ShaderProgramManager;
|
|
||||||
|
|
||||||
enum class UniformBindings : u32 { Common, VS, GS };
|
enum class UniformBindings : u32 { Common, VS, GS };
|
||||||
|
|
||||||
@ -112,36 +111,7 @@ static_assert(sizeof(VSUniformData) < 16384,
|
|||||||
|
|
||||||
struct ScreenInfo;
|
struct ScreenInfo;
|
||||||
|
|
||||||
class RasterizerVulkan : public VideoCore::RasterizerInterface {
|
struct VertexBase {
|
||||||
public:
|
|
||||||
explicit RasterizerVulkan(Frontend::EmuWindow& emu_window);
|
|
||||||
~RasterizerVulkan() override = default;
|
|
||||||
|
|
||||||
void LoadDiskResources(const std::atomic_bool& stop_loading,
|
|
||||||
const VideoCore::DiskResourceLoadCallback& callback) override;
|
|
||||||
|
|
||||||
void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1,
|
|
||||||
const Pica::Shader::OutputVertex& v2) override;
|
|
||||||
void DrawTriangles() override;
|
|
||||||
void NotifyPicaRegisterChanged(u32 id) override;
|
|
||||||
void FlushAll() override;
|
|
||||||
void FlushRegion(PAddr addr, u32 size) override;
|
|
||||||
void InvalidateRegion(PAddr addr, u32 size) override;
|
|
||||||
void FlushAndInvalidateRegion(PAddr addr, u32 size) override;
|
|
||||||
void ClearAll(bool flush) override;
|
|
||||||
bool AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) override;
|
|
||||||
bool AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) override;
|
|
||||||
bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config) override;
|
|
||||||
bool AccelerateDisplay(const GPU::Regs::FramebufferConfig& config, PAddr framebuffer_addr,
|
|
||||||
u32 pixel_stride, Vulkan::ScreenInfo& screen_info) override;
|
|
||||||
bool AccelerateDrawBatch(bool is_indexed) override;
|
|
||||||
|
|
||||||
/// Syncs entire status to match PICA registers
|
|
||||||
void SyncEntireState() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct VertexBase
|
|
||||||
{
|
|
||||||
VertexBase() = default;
|
VertexBase() = default;
|
||||||
VertexBase(const Pica::Shader::OutputVertex& v, bool flip_quaternion) {
|
VertexBase(const Pica::Shader::OutputVertex& v, bool flip_quaternion) {
|
||||||
position[0] = v.pos.x.ToFloat32();
|
position[0] = v.pos.x.ToFloat32();
|
||||||
@ -183,8 +153,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Structure that the hardware rendered vertices are composed of
|
/// Structure that the hardware rendered vertices are composed of
|
||||||
struct HardwareVertex : public VertexBase
|
struct HardwareVertex : public VertexBase {
|
||||||
{
|
|
||||||
HardwareVertex() = default;
|
HardwareVertex() = default;
|
||||||
HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) : VertexBase(v, flip_quaternion) {};
|
HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) : VertexBase(v, flip_quaternion) {};
|
||||||
static constexpr auto binding_desc = vk::VertexInputBindingDescription(0, sizeof(VertexBase));
|
static constexpr auto binding_desc = vk::VertexInputBindingDescription(0, sizeof(VertexBase));
|
||||||
@ -201,7 +170,34 @@ private:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RasterizerVulkan : public VideoCore::RasterizerInterface {
|
||||||
|
public:
|
||||||
|
explicit RasterizerVulkan(Frontend::EmuWindow& emu_window);
|
||||||
|
~RasterizerVulkan() override = default;
|
||||||
|
|
||||||
|
void LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||||
|
const VideoCore::DiskResourceLoadCallback& callback) override;
|
||||||
|
|
||||||
|
void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1,
|
||||||
|
const Pica::Shader::OutputVertex& v2) override;
|
||||||
|
void DrawTriangles() override;
|
||||||
|
void NotifyPicaRegisterChanged(u32 id) override;
|
||||||
|
void FlushAll() override;
|
||||||
|
void FlushRegion(PAddr addr, u32 size) override;
|
||||||
|
void InvalidateRegion(PAddr addr, u32 size) override;
|
||||||
|
void FlushAndInvalidateRegion(PAddr addr, u32 size) override;
|
||||||
|
void ClearAll(bool flush) override;
|
||||||
|
bool AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) override;
|
||||||
|
bool AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) override;
|
||||||
|
bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config) override;
|
||||||
|
bool AccelerateDisplay(const GPU::Regs::FramebufferConfig& config, PAddr framebuffer_addr,
|
||||||
|
u32 pixel_stride, Vulkan::ScreenInfo& screen_info) override;
|
||||||
|
bool AccelerateDrawBatch(bool is_indexed) override;
|
||||||
|
|
||||||
|
/// Syncs entire status to match PICA registers
|
||||||
|
void SyncEntireState() override;
|
||||||
|
|
||||||
|
private:
|
||||||
/// Syncs the clip enabled status to match the PICA register
|
/// Syncs the clip enabled status to match the PICA register
|
||||||
void SyncClipEnabled();
|
void SyncClipEnabled();
|
||||||
|
|
||||||
@ -337,8 +333,6 @@ private:
|
|||||||
bool dirty;
|
bool dirty;
|
||||||
} uniform_block_data = {};
|
} uniform_block_data = {};
|
||||||
|
|
||||||
std::unique_ptr<ShaderProgramManager> shader_program_manager;
|
|
||||||
|
|
||||||
// They shall be big enough for about one frame.
|
// They shall be big enough for about one frame.
|
||||||
static constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024;
|
static constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||||
static constexpr std::size_t INDEX_BUFFER_SIZE = 1 * 1024 * 1024;
|
static constexpr std::size_t INDEX_BUFFER_SIZE = 1 * 1024 * 1024;
|
||||||
|
1616
src/video_core/renderer_vulkan/vk_shader_gen.cpp
Normal file
1616
src/video_core/renderer_vulkan/vk_shader_gen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
43
src/video_core/renderer_vulkan/vk_shader_gen.h
Normal file
43
src/video_core/renderer_vulkan/vk_shader_gen.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2022 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include "common/hash.h"
|
||||||
|
#include "video_core/regs.h"
|
||||||
|
#include "video_core/shader/shader.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_shader_state.h"
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
|
||||||
|
namespace ShaderDecompiler {
|
||||||
|
struct ProgramResult {
|
||||||
|
std::string code;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the GLSL vertex shader program source code that accepts vertices from software shader
|
||||||
|
* and directly passes them to the fragment shader.
|
||||||
|
* @param separable_shader generates shader that can be used for separate shader object
|
||||||
|
* @returns String of the shader source code
|
||||||
|
*/
|
||||||
|
ShaderDecompiler::ProgramResult GenerateTrivialVertexShader(bool separable_shader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the GLSL fragment shader program source code for the current Pica state
|
||||||
|
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
|
||||||
|
* configuration (NOTE: Use state in this struct only, not the Pica registers!)
|
||||||
|
* @param separable_shader generates shader that can be used for separate shader object
|
||||||
|
* @returns String of the shader source code
|
||||||
|
*/
|
||||||
|
ShaderDecompiler::ProgramResult GenerateFragmentShader(const PicaFSConfig& config);
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
@ -10,6 +10,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
#include "common/hash.h"
|
#include "common/hash.h"
|
||||||
#include "video_core/regs.h"
|
#include "video_core/regs.h"
|
||||||
#include "video_core/shader/shader.h"
|
#include "video_core/shader/shader.h"
|
||||||
@ -198,17 +199,14 @@ struct PicaFixedGSConfig : Common::HashableStruct<PicaGSConfigCommonRaw> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
struct PipelineCacheKey {
|
||||||
* This struct combines the vertex and fragment states for a complete pipeline cache key
|
vk::PipelineColorBlendAttachmentState blend_config;
|
||||||
*/
|
|
||||||
struct VKPipelineCacheKey {
|
|
||||||
VKPipelineCacheKey(const Pica::Regs& regs, Pica::Shader::ShaderSetup& setup) :
|
|
||||||
vertex_config(regs.vs, setup), geometry_config(regs),
|
|
||||||
fragment_config(PicaFSConfig::BuildFromRegs(regs)) {}
|
|
||||||
|
|
||||||
PicaVSConfig vertex_config;
|
|
||||||
PicaFixedGSConfig geometry_config;
|
|
||||||
PicaFSConfig fragment_config;
|
PicaFSConfig fragment_config;
|
||||||
|
|
||||||
|
u64 Hash() const {
|
||||||
|
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(PipelineCacheKey));
|
||||||
|
return static_cast<size_t>(hash);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
@ -234,4 +232,11 @@ struct hash<Vulkan::PicaFixedGSConfig> {
|
|||||||
return k.Hash();
|
return k.Hash();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<Vulkan::PipelineCacheKey> {
|
||||||
|
size_t operator()(const Vulkan::PipelineCacheKey& k) const noexcept {
|
||||||
|
return k.Hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
#include "video_core/renderer_vulkan/vk_state.h"
|
#include "video_core/renderer_vulkan/vk_state.h"
|
||||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_resource_cache.h"
|
#include "video_core/renderer_vulkan/vk_resource_cache.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
#include "video_core/renderer_vulkan/vk_shader_gen.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
@ -67,20 +67,57 @@ void VulkanState::Create() {
|
|||||||
|
|
||||||
// Create texture sampler
|
// Create texture sampler
|
||||||
auto props = g_vk_instace->GetPhysicalDevice().getProperties();
|
auto props = g_vk_instace->GetPhysicalDevice().getProperties();
|
||||||
vk::SamplerCreateInfo sampler_info
|
vk::SamplerCreateInfo sampler_info{
|
||||||
(
|
|
||||||
{}, vk::Filter::eNearest, vk::Filter::eNearest,
|
{}, vk::Filter::eNearest, vk::Filter::eNearest,
|
||||||
vk::SamplerMipmapMode::eNearest, vk::SamplerAddressMode::eClampToEdge,
|
vk::SamplerMipmapMode::eNearest, vk::SamplerAddressMode::eClampToEdge,
|
||||||
vk::SamplerAddressMode::eClampToEdge, vk::SamplerAddressMode::eClampToEdge,
|
vk::SamplerAddressMode::eClampToEdge, vk::SamplerAddressMode::eClampToEdge,
|
||||||
{}, true, props.limits.maxSamplerAnisotropy,
|
{}, true, props.limits.maxSamplerAnisotropy,
|
||||||
false, vk::CompareOp::eAlways, {}, {},
|
false, vk::CompareOp::eAlways, {}, {},
|
||||||
vk::BorderColor::eIntOpaqueBlack, false
|
vk::BorderColor::eIntOpaqueBlack, false
|
||||||
);
|
};
|
||||||
|
|
||||||
sampler = g_vk_instace->GetDevice().createSamplerUnique(sampler_info);
|
sampler = g_vk_instace->GetDevice().createSamplerUnique(sampler_info);
|
||||||
dirty_flags |= DirtyFlags::All;
|
|
||||||
|
|
||||||
CompileTrivialShader();
|
|
||||||
|
// Define the descriptor sets we will be using
|
||||||
|
std::array<vk::DescriptorSetLayoutBinding, 2> ubo_set = {{
|
||||||
|
{ 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex |
|
||||||
|
vk::ShaderStageFlagBits::eGeometry | vk::ShaderStageFlagBits::eFragment }, // shader_data
|
||||||
|
{ 1, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex } // pica_uniforms
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::array<vk::DescriptorSetLayoutBinding, 4> texture_set = {{
|
||||||
|
{ 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex0
|
||||||
|
{ 1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex1
|
||||||
|
{ 2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex2
|
||||||
|
{ 3, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex_cube
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::array<vk::DescriptorSetLayoutBinding, 3> lut_set = {{
|
||||||
|
{ 0, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_lf
|
||||||
|
{ 1, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_rg
|
||||||
|
{ 2, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment } // texture_buffer_lut_rgba
|
||||||
|
}};
|
||||||
|
|
||||||
|
// Create and store descriptor set layouts
|
||||||
|
std::array<vk::DescriptorSetLayoutCreateInfo, DESCRIPTOR_SET_LAYOUT_COUNT> create_infos = {{
|
||||||
|
{ vk::DescriptorSetLayoutCreateFlags(), ubo_set },
|
||||||
|
{ vk::DescriptorSetLayoutCreateFlags(), texture_set },
|
||||||
|
{ vk::DescriptorSetLayoutCreateFlags(), lut_set }
|
||||||
|
}};
|
||||||
|
|
||||||
|
for (int i = 0; i < DESCRIPTOR_SET_LAYOUT_COUNT; i++) {
|
||||||
|
descriptor_layouts[i] = g_vk_instace->GetDevice().createDescriptorSetLayout(create_infos[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the standard descriptor set layout
|
||||||
|
vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts);
|
||||||
|
pipeline_layout = g_vk_instace->GetDevice().createPipelineLayoutUnique(layout_info);
|
||||||
|
|
||||||
|
// Compile trivial vertex shader
|
||||||
|
auto source = GenerateTrivialVertexShader(true);
|
||||||
|
MakeShader(source.code, vk::ShaderStageFlagBits::eVertex);
|
||||||
|
|
||||||
|
dirty_flags |= DirtyFlags::All;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanState::SetVertexBuffer(VKBuffer* buffer, vk::DeviceSize offset) {
|
void VulkanState::SetVertexBuffer(VKBuffer* buffer, vk::DeviceSize offset) {
|
||||||
@ -231,11 +268,11 @@ void VulkanState::SetLogicOp(vk::LogicOp new_logic_op) {
|
|||||||
|
|
||||||
void VulkanState::SetColorMask(bool red, bool green, bool blue, bool alpha) {
|
void VulkanState::SetColorMask(bool red, bool green, bool blue, bool alpha) {
|
||||||
auto mask = static_cast<vk::ColorComponentFlags>(red | (green << 1) | (blue << 2) | (alpha << 3));
|
auto mask = static_cast<vk::ColorComponentFlags>(red | (green << 1) | (blue << 2) | (alpha << 3));
|
||||||
static_state.blend.setColorWriteMask(mask);
|
pipeline_key.blend_config.setColorWriteMask(mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanState::SetBlendEnable(bool enable) {
|
void VulkanState::SetBlendEnable(bool enable) {
|
||||||
static_state.blend.setBlendEnable(enable);
|
pipeline_key.blend_config.setBlendEnable(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanState::SetBlendCostants(float red, float green, float blue, float alpha) {
|
void VulkanState::SetBlendCostants(float red, float green, float blue, float alpha) {
|
||||||
@ -286,7 +323,7 @@ void VulkanState::SetDepthTest(bool enable, vk::CompareOp compare) {
|
|||||||
|
|
||||||
void VulkanState::SetBlendOp(vk::BlendOp rgb_op, vk::BlendOp alpha_op, vk::BlendFactor src_color,
|
void VulkanState::SetBlendOp(vk::BlendOp rgb_op, vk::BlendOp alpha_op, vk::BlendFactor src_color,
|
||||||
vk::BlendFactor dst_color, vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha) {
|
vk::BlendFactor dst_color, vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha) {
|
||||||
auto& blend = static_state.blend;
|
auto& blend = pipeline_key.blend_config;
|
||||||
blend.setColorBlendOp(rgb_op);
|
blend.setColorBlendOp(rgb_op);
|
||||||
blend.setAlphaBlendOp(alpha_op);
|
blend.setAlphaBlendOp(alpha_op);
|
||||||
blend.setSrcColorBlendFactor(src_color);
|
blend.setSrcColorBlendFactor(src_color);
|
||||||
@ -295,6 +332,136 @@ void VulkanState::SetBlendOp(vk::BlendOp rgb_op, vk::BlendOp alpha_op, vk::Blend
|
|||||||
blend.setDstAlphaBlendFactor(dst_alpha);
|
blend.setDstAlphaBlendFactor(dst_alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VulkanState::SetFragmentShader(const Pica::Regs& regs) {
|
||||||
|
vk::Pipeline pipeline;
|
||||||
|
pipeline_key.fragment_config = PicaFSConfig::BuildFromRegs(regs);
|
||||||
|
auto it1 = pipelines.find(pipeline_key);
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Try to use an already complete pipeline
|
||||||
|
if (it1 != pipelines.end()) {
|
||||||
|
pipeline = it1->second.get();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe the shader has been compiled but the pipeline state changed?
|
||||||
|
auto shader = fragment_shaders.find(pipeline_key.fragment_config);
|
||||||
|
if (shader != fragment_shaders.end()) {
|
||||||
|
pipeline = MakePipeline(shader->second.get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-compile shader module and create new pipeline
|
||||||
|
auto result = GenerateFragmentShader(pipeline_key.fragment_config);
|
||||||
|
auto module = MakeShader(result.code, vk::ShaderStageFlagBits::eFragment);
|
||||||
|
pipeline = MakePipeline(module);
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
// Bind the pipeline
|
||||||
|
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||||
|
command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ShaderModule VulkanState::MakeShader(const std::string& source, vk::ShaderStageFlagBits stage) {
|
||||||
|
shaderc::Compiler compiler;
|
||||||
|
shaderc::CompileOptions options;
|
||||||
|
options.SetOptimizationLevel(shaderc_optimization_level_performance);
|
||||||
|
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
|
||||||
|
|
||||||
|
shaderc_shader_kind kind{};
|
||||||
|
std::string name{};
|
||||||
|
switch (stage) {
|
||||||
|
case vk::ShaderStageFlagBits::eVertex:
|
||||||
|
kind = shaderc_glsl_vertex_shader;
|
||||||
|
name = "vertex shader";
|
||||||
|
break;
|
||||||
|
case vk::ShaderStageFlagBits::eFragment:
|
||||||
|
kind = shaderc_glsl_fragment_shader;
|
||||||
|
name = "fragment shader";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_CRITICAL(Render_Vulkan, "Unknown shader stage");
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto shader_module = compiler.CompileGlslToSpv(source.data(), kind, name.c_str(), options);
|
||||||
|
if (shader_module.GetCompilationStatus() != shaderc_compilation_status_success) {
|
||||||
|
LOG_CRITICAL(Render_Vulkan, shader_module.GetErrorMessage().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto shader_code = std::vector<u32>{shader_module.cbegin(), shader_module.cend()};
|
||||||
|
vk::ShaderModuleCreateInfo shader_info{{}, shader_code};
|
||||||
|
|
||||||
|
auto& device = g_vk_instace->GetDevice();
|
||||||
|
auto shader = device.createShaderModuleUnique(shader_info);
|
||||||
|
|
||||||
|
if (stage == vk::ShaderStageFlagBits::eFragment) {
|
||||||
|
auto handle = shader.get();
|
||||||
|
fragment_shaders[pipeline_key.fragment_config] = std::move(shader);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
else if (stage == vk::ShaderStageFlagBits::eVertex) {
|
||||||
|
trivial_vertex_shader = std::move(shader);
|
||||||
|
return trivial_vertex_shader.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Pipeline VulkanState::MakePipeline(vk::ShaderModule fragment) {
|
||||||
|
std::array<vk::PipelineShaderStageCreateInfo, 2> shader_stages {{
|
||||||
|
{ {}, vk::ShaderStageFlagBits::eVertex, trivial_vertex_shader.get(), "main" },
|
||||||
|
{ {}, vk::ShaderStageFlagBits::eFragment, fragment, "main" }
|
||||||
|
}};
|
||||||
|
|
||||||
|
vk::PipelineVertexInputStateCreateInfo vertex_input_info{
|
||||||
|
{}, HardwareVertex::binding_desc, HardwareVertex::attribute_desc
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::PipelineInputAssemblyStateCreateInfo input_assembly{{}, vk::PrimitiveTopology::eTriangleList, false};
|
||||||
|
vk::PipelineRasterizationStateCreateInfo rasterizer{
|
||||||
|
{}, false, false, vk::PolygonMode::eFill, vk::CullModeFlagBits::eNone,
|
||||||
|
vk::FrontFace::eClockwise, false
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::PipelineMultisampleStateCreateInfo multisampling{{}, vk::SampleCountFlagBits::e1};
|
||||||
|
vk::PipelineColorBlendStateCreateInfo color_blending{{}, false, vk::LogicOp::eCopy, pipeline_key.blend_config};
|
||||||
|
|
||||||
|
// Enable every required dynamic state
|
||||||
|
std::array<vk::DynamicState, 14> dynamic_states{
|
||||||
|
vk::DynamicState::eDepthCompareOp, vk::DynamicState::eLineWidth,
|
||||||
|
vk::DynamicState::eDepthTestEnable, vk::DynamicState::eColorWriteEnableEXT,
|
||||||
|
vk::DynamicState::eStencilTestEnable, vk::DynamicState::eStencilOp,
|
||||||
|
vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask,
|
||||||
|
vk::DynamicState::eCullMode, vk::DynamicState::eBlendConstants,
|
||||||
|
vk::DynamicState::eViewport, vk::DynamicState::eScissor,
|
||||||
|
vk::DynamicState::eLogicOpEXT, vk::DynamicState::eFrontFace
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::PipelineDynamicStateCreateInfo dynamic_info{{}, dynamic_states};
|
||||||
|
|
||||||
|
vk::PipelineDepthStencilStateCreateInfo depth_info{
|
||||||
|
{}, true, true, vk::CompareOp::eGreaterOrEqual, false, true
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::GraphicsPipelineCreateInfo pipeline_info{
|
||||||
|
{}, shader_stages, &vertex_input_info, &input_assembly, nullptr, nullptr,
|
||||||
|
&rasterizer, &multisampling, &depth_info, &color_blending, &dynamic_info,
|
||||||
|
pipeline_layout.get(), nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
auto& device = g_vk_instace->GetDevice();
|
||||||
|
auto result = device.createGraphicsPipelineUnique(nullptr, pipeline_info);
|
||||||
|
|
||||||
|
if (result.result == vk::Result::eSuccess) {
|
||||||
|
auto handle = result.value.get();
|
||||||
|
pipelines[pipeline_key] = std::move(result.value);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanState::Apply() {
|
void VulkanState::Apply() {
|
||||||
// Update resources in descriptor sets if changed
|
// Update resources in descriptor sets if changed
|
||||||
UpdateDescriptorSet();
|
UpdateDescriptorSet();
|
||||||
@ -372,24 +539,4 @@ void VulkanState::UpdateDescriptorSet() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanState::CompileTrivialShader() {
|
|
||||||
auto source = OpenGL::GenerateTrivialVertexShader(true);
|
|
||||||
|
|
||||||
shaderc::Compiler compiler;
|
|
||||||
shaderc::CompileOptions options;
|
|
||||||
options.SetOptimizationLevel(shaderc_optimization_level_performance);
|
|
||||||
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
|
|
||||||
|
|
||||||
auto shader_module = compiler.CompileGlslToSpv(source.code, shaderc_glsl_vertex_shader, "vertex shader", options);
|
|
||||||
if (shader_module.GetCompilationStatus() != shaderc_compilation_status_success) {
|
|
||||||
LOG_CRITICAL(Render_Vulkan, shader_module.GetErrorMessage().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto shader_code = std::vector<u32>{ shader_module.cbegin(), shader_module.cend() };
|
|
||||||
vk::ShaderModuleCreateInfo shader_info { {}, shader_code };
|
|
||||||
|
|
||||||
auto& device = g_vk_instace->GetDevice();
|
|
||||||
trivial_vertex_shader = device.createShaderModuleUnique(shader_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <xxhash.h>
|
||||||
|
#include "video_core/regs.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_shader_state.h"
|
||||||
#include "video_core/renderer_vulkan/vk_texture.h"
|
#include "video_core/renderer_vulkan/vk_texture.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
@ -87,6 +90,7 @@ public:
|
|||||||
void PushRenderTargets(VKTexture* color, VKTexture* depth_stencil);
|
void PushRenderTargets(VKTexture* color, VKTexture* depth_stencil);
|
||||||
void PopRenderTargets();
|
void PopRenderTargets();
|
||||||
void SetRenderArea(vk::Rect2D render_area);
|
void SetRenderArea(vk::Rect2D render_area);
|
||||||
|
void SetFragmentShader(const Pica::Regs& config);
|
||||||
void BeginRendering();
|
void BeginRendering();
|
||||||
void EndRendering();
|
void EndRendering();
|
||||||
|
|
||||||
@ -102,8 +106,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateDescriptorSet();
|
void UpdateDescriptorSet();
|
||||||
void GetPipeline();
|
vk::Pipeline MakePipeline(vk::ShaderModule fragment);
|
||||||
void CompileTrivialShader();
|
vk::ShaderModule MakeShader(const std::string& source, vk::ShaderStageFlagBits stage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Binding {
|
struct Binding {
|
||||||
@ -145,13 +149,14 @@ private:
|
|||||||
vk::StencilOp fail_op, pass_op, depth_fail_op;
|
vk::StencilOp fail_op, pass_op, depth_fail_op;
|
||||||
vk::CompareOp compare_op;
|
vk::CompareOp compare_op;
|
||||||
|
|
||||||
struct {
|
|
||||||
vk::PipelineColorBlendAttachmentState blend;
|
|
||||||
vk::PipelineDepthStencilStateCreateInfo depth_stencil;
|
|
||||||
} static_state;
|
|
||||||
|
|
||||||
// Pipeline cache
|
// Pipeline cache
|
||||||
vk::UniqueShaderModule trivial_vertex_shader;
|
vk::UniqueShaderModule trivial_vertex_shader;
|
||||||
|
vk::UniquePipelineLayout pipeline_layout;
|
||||||
|
std::vector<vk::DescriptorSetLayout> descriptor_layouts;
|
||||||
|
PipelineCacheKey pipeline_key;
|
||||||
|
|
||||||
|
std::unordered_map<PicaFSConfig, vk::UniqueShaderModule> fragment_shaders;
|
||||||
|
std::unordered_map<PipelineCacheKey, vk::UniquePipeline> pipelines;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
Reference in New Issue
Block a user