|
|
@ -15,6 +15,12 @@
|
|
|
|
#include "video_core/shader_notify.h"
|
|
|
|
#include "video_core/shader_notify.h"
|
|
|
|
#include "video_core/texture_cache/texture_cache.h"
|
|
|
|
#include "video_core/texture_cache/texture_cache.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(_MSC_VER) && defined(NDEBUG)
|
|
|
|
|
|
|
|
#define LAMBDA_FORCEINLINE [[msvc::forceinline]]
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#define LAMBDA_FORCEINLINE
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
namespace OpenGL {
|
|
|
|
namespace OpenGL {
|
|
|
|
namespace {
|
|
|
|
namespace {
|
|
|
|
using Shader::ImageBufferDescriptor;
|
|
|
|
using Shader::ImageBufferDescriptor;
|
|
|
@ -98,13 +104,76 @@ std::pair<GLint, GLint> TransformFeedbackEnum(u8 location) {
|
|
|
|
return {GL_POSITION, 0};
|
|
|
|
return {GL_POSITION, 0};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Spec {
|
|
|
|
template <typename Spec>
|
|
|
|
|
|
|
|
bool Passes(const std::array<Shader::Info, 5>& stage_infos, u32 enabled_mask) {
|
|
|
|
|
|
|
|
for (size_t stage = 0; stage < stage_infos.size(); ++stage) {
|
|
|
|
|
|
|
|
if (!Spec::enabled_stages[stage] && ((enabled_mask >> stage) & 1) != 0) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto& info{stage_infos[stage]};
|
|
|
|
|
|
|
|
if constexpr (!Spec::has_storage_buffers) {
|
|
|
|
|
|
|
|
if (!info.storage_buffers_descriptors.empty()) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (!Spec::has_texture_buffers) {
|
|
|
|
|
|
|
|
if (!info.texture_buffer_descriptors.empty()) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (!Spec::has_image_buffers) {
|
|
|
|
|
|
|
|
if (!info.image_buffer_descriptors.empty()) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (!Spec::has_images) {
|
|
|
|
|
|
|
|
if (!info.image_descriptors.empty()) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using ConfigureFuncPtr = void (*)(GraphicsPipeline*, bool);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename Spec, typename... Specs>
|
|
|
|
|
|
|
|
ConfigureFuncPtr FindSpec(const std::array<Shader::Info, 5>& stage_infos, u32 enabled_mask) {
|
|
|
|
|
|
|
|
if constexpr (sizeof...(Specs) > 0) {
|
|
|
|
|
|
|
|
if (!Passes<Spec>(stage_infos, enabled_mask)) {
|
|
|
|
|
|
|
|
return FindSpec<Specs...>(stage_infos, enabled_mask);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return GraphicsPipeline::MakeConfigureSpecFunc<Spec>();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct SimpleVertexFragmentSpec {
|
|
|
|
|
|
|
|
static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, true};
|
|
|
|
|
|
|
|
static constexpr bool has_storage_buffers = false;
|
|
|
|
|
|
|
|
static constexpr bool has_texture_buffers = false;
|
|
|
|
|
|
|
|
static constexpr bool has_image_buffers = false;
|
|
|
|
|
|
|
|
static constexpr bool has_images = false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct SimpleVertexSpec {
|
|
|
|
|
|
|
|
static constexpr std::array<bool, 5> enabled_stages{true, false, false, false, false};
|
|
|
|
|
|
|
|
static constexpr bool has_storage_buffers = false;
|
|
|
|
|
|
|
|
static constexpr bool has_texture_buffers = false;
|
|
|
|
|
|
|
|
static constexpr bool has_image_buffers = false;
|
|
|
|
|
|
|
|
static constexpr bool has_images = false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct DefaultSpec {
|
|
|
|
static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true};
|
|
|
|
static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true};
|
|
|
|
static constexpr bool has_storage_buffers = true;
|
|
|
|
static constexpr bool has_storage_buffers = true;
|
|
|
|
static constexpr bool has_texture_buffers = true;
|
|
|
|
static constexpr bool has_texture_buffers = true;
|
|
|
|
static constexpr bool has_image_buffers = true;
|
|
|
|
static constexpr bool has_image_buffers = true;
|
|
|
|
static constexpr bool has_images = true;
|
|
|
|
static constexpr bool has_images = true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ConfigureFuncPtr ConfigureFunc(const std::array<Shader::Info, 5>& infos, u32 enabled_mask) {
|
|
|
|
|
|
|
|
return FindSpec<SimpleVertexSpec, SimpleVertexFragmentSpec, DefaultSpec>(infos, enabled_mask);
|
|
|
|
|
|
|
|
}
|
|
|
|
} // Anonymous namespace
|
|
|
|
} // Anonymous namespace
|
|
|
|
|
|
|
|
|
|
|
|
size_t GraphicsPipelineKey::Hash() const noexcept {
|
|
|
|
size_t GraphicsPipelineKey::Hash() const noexcept {
|
|
|
@ -129,40 +198,21 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
|
|
|
|
if (shader_notify) {
|
|
|
|
if (shader_notify) {
|
|
|
|
shader_notify->MarkShaderBuilding();
|
|
|
|
shader_notify->MarkShaderBuilding();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::ranges::transform(infos, stage_infos.begin(),
|
|
|
|
|
|
|
|
[](const Shader::Info* info) { return info ? *info : Shader::Info{}; });
|
|
|
|
|
|
|
|
auto func{[this, device, sources, shader_notify, xfb_state](ShaderContext::Context*) mutable {
|
|
|
|
|
|
|
|
if (!device.UseAssemblyShaders()) {
|
|
|
|
|
|
|
|
program.handle = glCreateProgram();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t stage = 0; stage < 5; ++stage) {
|
|
|
|
|
|
|
|
const auto code{sources[stage]};
|
|
|
|
|
|
|
|
if (code.empty()) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (device.UseAssemblyShaders()) {
|
|
|
|
|
|
|
|
assembly_programs[stage] = CompileProgram(code, AssemblyStage(stage));
|
|
|
|
|
|
|
|
enabled_stages_mask |= (assembly_programs[stage].handle != 0 ? 1 : 0) << stage;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
AttachShader(Stage(stage), program.handle, code);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!device.UseAssemblyShaders()) {
|
|
|
|
|
|
|
|
LinkProgram(program.handle);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 num_textures{};
|
|
|
|
u32 num_textures{};
|
|
|
|
u32 num_images{};
|
|
|
|
u32 num_images{};
|
|
|
|
u32 num_storage_buffers{};
|
|
|
|
u32 num_storage_buffers{};
|
|
|
|
for (size_t stage = 0; stage < base_uniform_bindings.size(); ++stage) {
|
|
|
|
for (size_t stage = 0; stage < base_uniform_bindings.size(); ++stage) {
|
|
|
|
const auto& info{stage_infos[stage]};
|
|
|
|
auto& info{stage_infos[stage]};
|
|
|
|
|
|
|
|
if (infos[stage]) {
|
|
|
|
|
|
|
|
info = *infos[stage];
|
|
|
|
|
|
|
|
enabled_stages_mask |= 1u << stage;
|
|
|
|
|
|
|
|
}
|
|
|
|
if (stage < 4) {
|
|
|
|
if (stage < 4) {
|
|
|
|
base_uniform_bindings[stage + 1] = base_uniform_bindings[stage];
|
|
|
|
base_uniform_bindings[stage + 1] = base_uniform_bindings[stage];
|
|
|
|
base_storage_bindings[stage + 1] = base_storage_bindings[stage];
|
|
|
|
base_storage_bindings[stage + 1] = base_storage_bindings[stage];
|
|
|
|
|
|
|
|
|
|
|
|
base_uniform_bindings[stage + 1] +=
|
|
|
|
base_uniform_bindings[stage + 1] += AccumulateCount(info.constant_buffer_descriptors);
|
|
|
|
AccumulateCount(info.constant_buffer_descriptors);
|
|
|
|
base_storage_bindings[stage + 1] += AccumulateCount(info.storage_buffers_descriptors);
|
|
|
|
base_storage_bindings[stage + 1] +=
|
|
|
|
|
|
|
|
AccumulateCount(info.storage_buffers_descriptors);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enabled_uniform_buffer_masks[stage] = info.constant_buffer_mask;
|
|
|
|
enabled_uniform_buffer_masks[stage] = info.constant_buffer_mask;
|
|
|
|
std::ranges::copy(info.constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
|
|
|
|
std::ranges::copy(info.constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
|
|
|
@ -189,10 +239,29 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
|
|
|
|
use_storage_buffers =
|
|
|
|
use_storage_buffers =
|
|
|
|
!assembly_shaders || num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
|
|
|
|
!assembly_shaders || num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks();
|
|
|
|
writes_global_memory &= !use_storage_buffers;
|
|
|
|
writes_global_memory &= !use_storage_buffers;
|
|
|
|
|
|
|
|
configure_func = ConfigureFunc(stage_infos, enabled_stages_mask);
|
|
|
|
|
|
|
|
|
|
|
|
if (assembly_shaders && xfb_state) {
|
|
|
|
if (assembly_shaders && xfb_state) {
|
|
|
|
GenerateTransformFeedbackState(*xfb_state);
|
|
|
|
GenerateTransformFeedbackState(*xfb_state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto func{[this, device, sources, shader_notify, xfb_state](ShaderContext::Context*) mutable {
|
|
|
|
|
|
|
|
if (!device.UseAssemblyShaders()) {
|
|
|
|
|
|
|
|
program.handle = glCreateProgram();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t stage = 0; stage < 5; ++stage) {
|
|
|
|
|
|
|
|
const auto code{sources[stage]};
|
|
|
|
|
|
|
|
if (code.empty()) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (device.UseAssemblyShaders()) {
|
|
|
|
|
|
|
|
assembly_programs[stage] = CompileProgram(code, AssemblyStage(stage));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
AttachShader(Stage(stage), program.handle, code);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!device.UseAssemblyShaders()) {
|
|
|
|
|
|
|
|
LinkProgram(program.handle);
|
|
|
|
|
|
|
|
}
|
|
|
|
if (shader_notify) {
|
|
|
|
if (shader_notify) {
|
|
|
|
shader_notify->MarkShaderComplete();
|
|
|
|
shader_notify->MarkShaderComplete();
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -205,7 +274,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GraphicsPipeline::Configure(bool is_indexed) {
|
|
|
|
template <typename Spec>
|
|
|
|
|
|
|
|
void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
|
|
|
|
std::array<ImageId, MAX_TEXTURES + MAX_IMAGES> image_view_ids;
|
|
|
|
std::array<ImageId, MAX_TEXTURES + MAX_IMAGES> image_view_ids;
|
|
|
|
std::array<u32, MAX_TEXTURES + MAX_IMAGES> image_view_indices;
|
|
|
|
std::array<u32, MAX_TEXTURES + MAX_IMAGES> image_view_indices;
|
|
|
|
std::array<GLuint, MAX_TEXTURES> samplers;
|
|
|
|
std::array<GLuint, MAX_TEXTURES> samplers;
|
|
|
@ -221,7 +291,7 @@ void GraphicsPipeline::Configure(bool is_indexed) {
|
|
|
|
|
|
|
|
|
|
|
|
const auto& regs{maxwell3d.regs};
|
|
|
|
const auto& regs{maxwell3d.regs};
|
|
|
|
const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex};
|
|
|
|
const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex};
|
|
|
|
const auto config_stage{[&](size_t stage) {
|
|
|
|
const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
|
|
|
|
const Shader::Info& info{stage_infos[stage]};
|
|
|
|
const Shader::Info& info{stage_infos[stage]};
|
|
|
|
buffer_cache.UnbindGraphicsStorageBuffers(stage);
|
|
|
|
buffer_cache.UnbindGraphicsStorageBuffers(stage);
|
|
|
|
if constexpr (Spec::has_storage_buffers) {
|
|
|
|
if constexpr (Spec::has_storage_buffers) {
|
|
|
@ -311,7 +381,7 @@ void GraphicsPipeline::Configure(bool is_indexed) {
|
|
|
|
state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle());
|
|
|
|
state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle());
|
|
|
|
|
|
|
|
|
|
|
|
ImageId* texture_buffer_index{image_view_ids.data()};
|
|
|
|
ImageId* texture_buffer_index{image_view_ids.data()};
|
|
|
|
const auto bind_stage_info{[&](size_t stage) {
|
|
|
|
const auto bind_stage_info{[&](size_t stage) LAMBDA_FORCEINLINE {
|
|
|
|
size_t index{};
|
|
|
|
size_t index{};
|
|
|
|
const auto add_buffer{[&](const auto& desc) {
|
|
|
|
const auto add_buffer{[&](const auto& desc) {
|
|
|
|
constexpr bool is_image = std::is_same_v<decltype(desc), const ImageBufferDescriptor&>;
|
|
|
|
constexpr bool is_image = std::is_same_v<decltype(desc), const ImageBufferDescriptor&>;
|
|
|
@ -430,6 +500,11 @@ void GraphicsPipeline::Configure(bool is_indexed) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
|
|
|
|
|
|
|
|
glTransformFeedbackStreamAttribsNV(num_xfb_attribs, xfb_attribs.data(), num_xfb_strides,
|
|
|
|
|
|
|
|
xfb_streams.data(), GL_INTERLEAVED_ATTRIBS);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GraphicsPipeline::GenerateTransformFeedbackState(
|
|
|
|
void GraphicsPipeline::GenerateTransformFeedbackState(
|
|
|
|
const VideoCommon::TransformFeedbackState& xfb_state) {
|
|
|
|
const VideoCommon::TransformFeedbackState& xfb_state) {
|
|
|
|
// TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
|
|
|
|
// TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
|
|
|
@ -475,9 +550,4 @@ void GraphicsPipeline::GenerateTransformFeedbackState(
|
|
|
|
num_xfb_strides = static_cast<GLsizei>(current_stream - xfb_streams.data());
|
|
|
|
num_xfb_strides = static_cast<GLsizei>(current_stream - xfb_streams.data());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
|
|
|
|
|
|
|
|
glTransformFeedbackStreamAttribsNV(num_xfb_attribs, xfb_attribs.data(), num_xfb_strides,
|
|
|
|
|
|
|
|
xfb_streams.data(), GL_INTERLEAVED_ATTRIBS);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace OpenGL
|
|
|
|
} // namespace OpenGL
|
|
|
|