mirror of https://github.com/yuzu-mirror/yuzu
shader: Initial OpenGL implementation
parent
850b08a16c
commit
d621e96d0d
@ -0,0 +1,178 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/cityhash.h"
|
||||
#include "video_core/renderer_opengl/gl_compute_program.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
using Shader::ImageBufferDescriptor;
|
||||
using Tegra::Texture::TexturePair;
|
||||
using VideoCommon::ImageId;
|
||||
|
||||
constexpr u32 MAX_TEXTURES = 64;
|
||||
constexpr u32 MAX_IMAGES = 16;
|
||||
|
||||
size_t ComputeProgramKey::Hash() const noexcept {
|
||||
return static_cast<size_t>(
|
||||
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this));
|
||||
}
|
||||
|
||||
bool ComputeProgramKey::operator==(const ComputeProgramKey& rhs) const noexcept {
|
||||
return std::memcmp(this, &rhs, sizeof *this) == 0;
|
||||
}
|
||||
|
||||
ComputeProgram::ComputeProgram(TextureCache& texture_cache_, BufferCache& buffer_cache_,
|
||||
Tegra::MemoryManager& gpu_memory_,
|
||||
Tegra::Engines::KeplerCompute& kepler_compute_,
|
||||
ProgramManager& program_manager_, OGLProgram program_,
|
||||
const Shader::Info& info_)
|
||||
: texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, gpu_memory{gpu_memory_},
|
||||
kepler_compute{kepler_compute_},
|
||||
program_manager{program_manager_}, program{std::move(program_)}, info{info_} {
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
num_texture_buffers += desc.count;
|
||||
}
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
num_image_buffers += desc.count;
|
||||
}
|
||||
u32 num_textures = num_texture_buffers;
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
num_textures += desc.count;
|
||||
}
|
||||
ASSERT(num_textures <= MAX_TEXTURES);
|
||||
|
||||
u32 num_images = num_image_buffers;
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
num_images += desc.count;
|
||||
}
|
||||
ASSERT(num_images <= MAX_IMAGES);
|
||||
}
|
||||
|
||||
void ComputeProgram::Configure() {
|
||||
buffer_cache.SetEnabledComputeUniformBuffers(info.constant_buffer_mask);
|
||||
buffer_cache.UnbindComputeStorageBuffers();
|
||||
size_t ssbo_index{};
|
||||
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||
ASSERT(desc.count == 1);
|
||||
buffer_cache.BindComputeStorageBuffer(ssbo_index, desc.cbuf_index, desc.cbuf_offset,
|
||||
desc.is_written);
|
||||
++ssbo_index;
|
||||
}
|
||||
texture_cache.SynchronizeComputeDescriptors();
|
||||
|
||||
std::array<ImageViewId, MAX_TEXTURES + MAX_IMAGES> image_view_ids;
|
||||
boost::container::static_vector<u32, MAX_TEXTURES + MAX_IMAGES> image_view_indices;
|
||||
std::array<GLuint, MAX_TEXTURES> samplers;
|
||||
std::array<GLuint, MAX_TEXTURES> textures;
|
||||
std::array<GLuint, MAX_IMAGES> images;
|
||||
GLsizei sampler_binding{};
|
||||
GLsizei texture_binding{};
|
||||
GLsizei image_binding{};
|
||||
|
||||
const auto& qmd{kepler_compute.launch_description};
|
||||
const auto& cbufs{qmd.const_buffer_config};
|
||||
const bool via_header_index{qmd.linked_tsc != 0};
|
||||
const auto read_handle{[&](const auto& desc, u32 index) {
|
||||
ASSERT(((qmd.const_buffer_enable_mask >> desc.cbuf_index) & 1) != 0);
|
||||
const u32 index_offset{index << desc.size_shift};
|
||||
const u32 offset{desc.cbuf_offset + index_offset};
|
||||
const GPUVAddr addr{cbufs[desc.cbuf_index].Address() + offset};
|
||||
if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> ||
|
||||
std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) {
|
||||
if (desc.has_secondary) {
|
||||
ASSERT(((qmd.const_buffer_enable_mask >> desc.secondary_cbuf_index) & 1) != 0);
|
||||
const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset};
|
||||
const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() +
|
||||
secondary_offset};
|
||||
const u32 lhs_raw{gpu_memory.Read<u32>(addr)};
|
||||
const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)};
|
||||
return TexturePair(lhs_raw | rhs_raw, via_header_index);
|
||||
}
|
||||
}
|
||||
return TexturePair(gpu_memory.Read<u32>(addr), via_header_index);
|
||||
}};
|
||||
const auto add_image{[&](const auto& desc) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
image_view_indices.push_back(handle.first);
|
||||
}
|
||||
}};
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
image_view_indices.push_back(handle.first);
|
||||
samplers[sampler_binding++] = 0;
|
||||
}
|
||||
}
|
||||
std::ranges::for_each(info.image_buffer_descriptors, add_image);
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
image_view_indices.push_back(handle.first);
|
||||
|
||||
Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
|
||||
samplers[sampler_binding++] = sampler->Handle();
|
||||
}
|
||||
}
|
||||
std::ranges::for_each(info.image_descriptors, add_image);
|
||||
|
||||
const std::span indices_span(image_view_indices.data(), image_view_indices.size());
|
||||
texture_cache.FillComputeImageViews(indices_span, image_view_ids);
|
||||
|
||||
buffer_cache.UnbindComputeTextureBuffers();
|
||||
size_t texbuf_index{};
|
||||
const auto add_buffer{[&](const auto& desc) {
|
||||
constexpr bool is_image = std::is_same_v<decltype(desc), const ImageBufferDescriptor&>;
|
||||
for (u32 i = 0; i < desc.count; ++i) {
|
||||
bool is_written{false};
|
||||
if constexpr (is_image) {
|
||||
is_written = desc.is_written;
|
||||
}
|
||||
ImageView& image_view{texture_cache.GetImageView(image_view_ids[texbuf_index])};
|
||||
buffer_cache.BindComputeTextureBuffer(texbuf_index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
is_written, is_image);
|
||||
++texbuf_index;
|
||||
}
|
||||
}};
|
||||
std::ranges::for_each(info.texture_buffer_descriptors, add_buffer);
|
||||
std::ranges::for_each(info.image_buffer_descriptors, add_buffer);
|
||||
|
||||
buffer_cache.UpdateComputeBuffers();
|
||||
|
||||
buffer_cache.runtime.SetImagePointers(textures.data(), images.data());
|
||||
buffer_cache.BindHostComputeBuffers();
|
||||
|
||||
const ImageId* views_it{image_view_ids.data() + num_texture_buffers + num_image_buffers};
|
||||
texture_binding += num_texture_buffers;
|
||||
image_binding += num_image_buffers;
|
||||
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
ImageView& image_view{texture_cache.GetImageView(*(views_it++))};
|
||||
textures[texture_binding++] = image_view.Handle(desc.type);
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
ImageView& image_view{texture_cache.GetImageView(*(views_it++))};
|
||||
images[image_binding++] = image_view.Handle(desc.type);
|
||||
}
|
||||
}
|
||||
if (texture_binding != 0) {
|
||||
ASSERT(texture_binding == sampler_binding);
|
||||
glBindTextures(0, texture_binding, textures.data());
|
||||
glBindSamplers(0, sampler_binding, samplers.data());
|
||||
}
|
||||
if (image_binding != 0) {
|
||||
glBindImageTextures(0, image_binding, images.data());
|
||||
}
|
||||
program_manager.BindProgram(program.handle);
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -0,0 +1,83 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
|
||||
namespace Tegra {
|
||||
class MemoryManager;
|
||||
}
|
||||
|
||||
namespace Tegra::Engines {
|
||||
class KeplerCompute;
|
||||
}
|
||||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class ProgramManager;
|
||||
|
||||
struct ComputeProgramKey {
|
||||
u64 unique_hash;
|
||||
u32 shared_memory_size;
|
||||
std::array<u32, 3> workgroup_size;
|
||||
|
||||
size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const ComputeProgramKey&) const noexcept;
|
||||
|
||||
bool operator!=(const ComputeProgramKey& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<ComputeProgramKey>);
|
||||
static_assert(std::is_trivially_copyable_v<ComputeProgramKey>);
|
||||
static_assert(std::is_trivially_constructible_v<ComputeProgramKey>);
|
||||
|
||||
class ComputeProgram {
|
||||
public:
|
||||
explicit ComputeProgram(TextureCache& texture_cache_, BufferCache& buffer_cache_,
|
||||
Tegra::MemoryManager& gpu_memory_,
|
||||
Tegra::Engines::KeplerCompute& kepler_compute_,
|
||||
ProgramManager& program_manager_, OGLProgram program_,
|
||||
const Shader::Info& info_);
|
||||
|
||||
void Configure();
|
||||
|
||||
private:
|
||||
TextureCache& texture_cache;
|
||||
BufferCache& buffer_cache;
|
||||
Tegra::MemoryManager& gpu_memory;
|
||||
Tegra::Engines::KeplerCompute& kepler_compute;
|
||||
ProgramManager& program_manager;
|
||||
|
||||
OGLProgram program;
|
||||
Shader::Info info;
|
||||
|
||||
u32 num_texture_buffers{};
|
||||
u32 num_image_buffers{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<OpenGL::ComputeProgramKey> {
|
||||
size_t operator()(const OpenGL::ComputeProgramKey& k) const noexcept {
|
||||
return k.Hash();
|
||||
}
|
||||
};
|
||||
} // namespace std
|
@ -0,0 +1,296 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/cityhash.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "video_core/renderer_opengl/gl_graphics_program.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
#include "video_core/texture_cache/texture_cache.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
using Shader::ImageBufferDescriptor;
|
||||
using Tegra::Texture::TexturePair;
|
||||
using VideoCommon::ImageId;
|
||||
|
||||
constexpr u32 MAX_TEXTURES = 64;
|
||||
constexpr u32 MAX_IMAGES = 8;
|
||||
|
||||
size_t GraphicsProgramKey::Hash() const noexcept {
|
||||
return static_cast<size_t>(Common::CityHash64(reinterpret_cast<const char*>(this), Size()));
|
||||
}
|
||||
|
||||
bool GraphicsProgramKey::operator==(const GraphicsProgramKey& rhs) const noexcept {
|
||||
return std::memcmp(this, &rhs, Size()) == 0;
|
||||
}
|
||||
|
||||
GraphicsProgram::GraphicsProgram(TextureCache& texture_cache_, BufferCache& buffer_cache_,
|
||||
Tegra::MemoryManager& gpu_memory_,
|
||||
Tegra::Engines::Maxwell3D& maxwell3d_,
|
||||
ProgramManager& program_manager_, StateTracker& state_tracker_,
|
||||
OGLProgram program_,
|
||||
const std::array<const Shader::Info*, 5>& infos)
|
||||
: texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
|
||||
gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, program_manager{program_manager_},
|
||||
state_tracker{state_tracker_}, program{std::move(program_)} {
|
||||
std::ranges::transform(infos, stage_infos.begin(),
|
||||
[](const Shader::Info* info) { return info ? *info : Shader::Info{}; });
|
||||
|
||||
u32 num_textures{};
|
||||
u32 num_images{};
|
||||
for (size_t stage = 0; stage < base_uniform_bindings.size() - 1; ++stage) {
|
||||
const auto& info{stage_infos[stage]};
|
||||
base_uniform_bindings[stage + 1] = base_uniform_bindings[stage];
|
||||
base_storage_bindings[stage + 1] = base_storage_bindings[stage];
|
||||
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||
base_uniform_bindings[stage + 1] += desc.count;
|
||||
}
|
||||
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||
base_storage_bindings[stage + 1] += desc.count;
|
||||
}
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
num_texture_buffers[stage] += desc.count;
|
||||
num_textures += desc.count;
|
||||
}
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
num_image_buffers[stage] += desc.count;
|
||||
num_images += desc.count;
|
||||
}
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
num_textures += desc.count;
|
||||
}
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
num_images += desc.count;
|
||||
}
|
||||
}
|
||||
ASSERT(num_textures <= MAX_TEXTURES);
|
||||
ASSERT(num_images <= MAX_IMAGES);
|
||||
}
|
||||
|
||||
struct Spec {
|
||||
static constexpr std::array<bool, 5> enabled_stages{true, true, true, true, true};
|
||||
static constexpr bool has_storage_buffers = true;
|
||||
static constexpr bool has_texture_buffers = true;
|
||||
static constexpr bool has_image_buffers = true;
|
||||
static constexpr bool has_images = true;
|
||||
};
|
||||
|
||||
void GraphicsProgram::Configure(bool is_indexed) {
|
||||
std::array<ImageId, MAX_TEXTURES + MAX_IMAGES> image_view_ids;
|
||||
std::array<u32, MAX_TEXTURES + MAX_IMAGES> image_view_indices;
|
||||
std::array<GLuint, MAX_TEXTURES> samplers;
|
||||
size_t image_view_index{};
|
||||
GLsizei sampler_binding{};
|
||||
|
||||
texture_cache.SynchronizeGraphicsDescriptors();
|
||||
|
||||
buffer_cache.runtime.SetBaseUniformBindings(base_uniform_bindings);
|
||||
buffer_cache.runtime.SetBaseStorageBindings(base_storage_bindings);
|
||||
|
||||
const auto& regs{maxwell3d.regs};
|
||||
const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex};
|
||||
const auto config_stage{[&](size_t stage) {
|
||||
const Shader::Info& info{stage_infos[stage]};
|
||||
buffer_cache.SetEnabledUniformBuffers(stage, info.constant_buffer_mask);
|
||||
buffer_cache.UnbindGraphicsStorageBuffers(stage);
|
||||
if constexpr (Spec::has_storage_buffers) {
|
||||
size_t ssbo_index{};
|
||||
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||
ASSERT(desc.count == 1);
|
||||
buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index,
|
||||
desc.cbuf_offset, desc.is_written);
|
||||
++ssbo_index;
|
||||
}
|
||||
}
|
||||
const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers};
|
||||
const auto read_handle{[&](const auto& desc, u32 index) {
|
||||
ASSERT(cbufs[desc.cbuf_index].enabled);
|
||||
const u32 index_offset{index << desc.size_shift};
|
||||
const u32 offset{desc.cbuf_offset + index_offset};
|
||||
const GPUVAddr addr{cbufs[desc.cbuf_index].address + offset};
|
||||
if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> ||
|
||||
std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) {
|
||||
if (desc.has_secondary) {
|
||||
ASSERT(cbufs[desc.secondary_cbuf_index].enabled);
|
||||
const u32 second_offset{desc.secondary_cbuf_offset + index_offset};
|
||||
const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address +
|
||||
second_offset};
|
||||
const u32 lhs_raw{gpu_memory.Read<u32>(addr)};
|
||||
const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)};
|
||||
const u32 raw{lhs_raw | rhs_raw};
|
||||
return TexturePair(raw, via_header_index);
|
||||
}
|
||||
}
|
||||
return TexturePair(gpu_memory.Read<u32>(addr), via_header_index);
|
||||
}};
|
||||
const auto add_image{[&](const auto& desc) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
image_view_indices[image_view_index++] = handle.first;
|
||||
}
|
||||
}};
|
||||
if constexpr (Spec::has_texture_buffers) {
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
image_view_indices[image_view_index++] = handle.first;
|
||||
samplers[sampler_binding++] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if constexpr (Spec::has_image_buffers) {
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
add_image(desc);
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
const auto handle{read_handle(desc, index)};
|
||||
image_view_indices[image_view_index++] = handle.first;
|
||||
|
||||
Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
|
||||
samplers[sampler_binding++] = sampler->Handle();
|
||||
}
|
||||
}
|
||||
if constexpr (Spec::has_images) {
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
add_image(desc);
|
||||
}
|
||||
}
|
||||
}};
|
||||
if constexpr (Spec::enabled_stages[0]) {
|
||||
config_stage(0);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[1]) {
|
||||
config_stage(1);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[2]) {
|
||||
config_stage(2);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[3]) {
|
||||
config_stage(3);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[4]) {
|
||||
config_stage(4);
|
||||
}
|
||||
const std::span indices_span(image_view_indices.data(), image_view_index);
|
||||
texture_cache.FillGraphicsImageViews(indices_span, image_view_ids);
|
||||
|
||||
ImageId* texture_buffer_index{image_view_ids.data()};
|
||||
const auto bind_stage_info{[&](size_t stage) {
|
||||
size_t index{};
|
||||
const auto add_buffer{[&](const auto& desc) {
|
||||
constexpr bool is_image = std::is_same_v<decltype(desc), const ImageBufferDescriptor&>;
|
||||
for (u32 i = 0; i < desc.count; ++i) {
|
||||
bool is_written{false};
|
||||
if constexpr (is_image) {
|
||||
is_written = desc.is_written;
|
||||
}
|
||||
ImageView& image_view{texture_cache.GetImageView(*texture_buffer_index)};
|
||||
buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
|
||||
image_view.BufferSize(), image_view.format,
|
||||
is_written, is_image);
|
||||
++index;
|
||||
++texture_buffer_index;
|
||||
}
|
||||
}};
|
||||
const Shader::Info& info{stage_infos[stage]};
|
||||
buffer_cache.UnbindGraphicsTextureBuffers(stage);
|
||||
|
||||
if constexpr (Spec::has_texture_buffers) {
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
add_buffer(desc);
|
||||
}
|
||||
}
|
||||
if constexpr (Spec::has_image_buffers) {
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
add_buffer(desc);
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
texture_buffer_index += desc.count;
|
||||
}
|
||||
if constexpr (Spec::has_images) {
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
texture_buffer_index += desc.count;
|
||||
}
|
||||
}
|
||||
}};
|
||||
if constexpr (Spec::enabled_stages[0]) {
|
||||
bind_stage_info(0);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[1]) {
|
||||
bind_stage_info(1);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[2]) {
|
||||
bind_stage_info(2);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[3]) {
|
||||
bind_stage_info(3);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[4]) {
|
||||
bind_stage_info(4);
|
||||
}
|
||||
buffer_cache.UpdateGraphicsBuffers(is_indexed);
|
||||
buffer_cache.BindHostGeometryBuffers(is_indexed);
|
||||
|
||||
const ImageId* views_it{image_view_ids.data()};
|
||||
GLsizei texture_binding = 0;
|
||||
GLsizei image_binding = 0;
|
||||
std::array<GLuint, MAX_TEXTURES> textures;
|
||||
std::array<GLuint, MAX_IMAGES> images;
|
||||
const auto prepare_stage{[&](size_t stage) {
|
||||
buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]);
|
||||
buffer_cache.BindHostStageBuffers(stage);
|
||||
|
||||
texture_binding += num_texture_buffers[stage];
|
||||
image_binding += num_image_buffers[stage];
|
||||
|
||||
const auto& info{stage_infos[stage]};
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
ImageView& image_view{texture_cache.GetImageView(*(views_it++))};
|
||||
textures[texture_binding++] = image_view.Handle(desc.type);
|
||||
}
|
||||
}
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
for (u32 index = 0; index < desc.count; ++index) {
|
||||
ImageView& image_view{texture_cache.GetImageView(*(views_it++))};
|
||||
images[image_binding++] = image_view.Handle(desc.type);
|
||||
}
|
||||
}
|
||||
}};
|
||||
if constexpr (Spec::enabled_stages[0]) {
|
||||
prepare_stage(0);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[1]) {
|
||||
prepare_stage(1);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[2]) {
|
||||
prepare_stage(2);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[3]) {
|
||||
prepare_stage(3);
|
||||
}
|
||||
if constexpr (Spec::enabled_stages[4]) {
|
||||
prepare_stage(4);
|
||||
}
|
||||
if (texture_binding != 0) {
|
||||
ASSERT(texture_binding == sampler_binding);
|
||||
glBindTextures(0, texture_binding, textures.data());
|
||||
glBindSamplers(0, sampler_binding, samplers.data());
|
||||
}
|
||||
if (image_binding != 0) {
|
||||
glBindImageTextures(0, image_binding, images.data());
|
||||
}
|
||||
texture_cache.UpdateRenderTargets(false);
|
||||
|
||||
state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle());
|
||||
program_manager.BindProgram(program.handle);
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -0,0 +1,105 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "shader_recompiler/shader_info.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class ProgramManager;
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
struct GraphicsProgramKey {
|
||||
struct TransformFeedbackState {
|
||||
struct Layout {
|
||||
u32 stream;
|
||||
u32 varying_count;
|
||||
u32 stride;
|
||||
};
|
||||
std::array<Layout, Maxwell::NumTransformFeedbackBuffers> layouts;
|
||||
std::array<std::array<u8, 128>, Maxwell::NumTransformFeedbackBuffers> varyings;
|
||||
};
|
||||
|
||||
std::array<u64, 6> unique_hashes;
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> xfb_enabled;
|
||||
BitField<1, 1, u32> early_z;
|
||||
BitField<2, 4, Maxwell::PrimitiveTopology> gs_input_topology;
|
||||
BitField<6, 2, Maxwell::TessellationPrimitive> tessellation_primitive;
|
||||
BitField<8, 2, Maxwell::TessellationSpacing> tessellation_spacing;
|
||||
BitField<10, 1, u32> tessellation_clockwise;
|
||||
};
|
||||
std::array<u32, 3> padding;
|
||||
TransformFeedbackState xfb_state;
|
||||
|
||||
size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const GraphicsProgramKey&) const noexcept;
|
||||
|
||||
bool operator!=(const GraphicsProgramKey& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t Size() const noexcept {
|
||||
if (xfb_enabled != 0) {
|
||||
return sizeof(GraphicsProgramKey);
|
||||
} else {
|
||||
return offsetof(GraphicsProgramKey, padding);
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<GraphicsProgramKey>);
|
||||
static_assert(std::is_trivially_copyable_v<GraphicsProgramKey>);
|
||||
static_assert(std::is_trivially_constructible_v<GraphicsProgramKey>);
|
||||
|
||||
class GraphicsProgram {
|
||||
public:
|
||||
explicit GraphicsProgram(TextureCache& texture_cache_, BufferCache& buffer_cache_,
|
||||
Tegra::MemoryManager& gpu_memory_,
|
||||
Tegra::Engines::Maxwell3D& maxwell3d_,
|
||||
ProgramManager& program_manager_, StateTracker& state_tracker_,
|
||||
OGLProgram program_, const std::array<const Shader::Info*, 5>& infos);
|
||||
|
||||
void Configure(bool is_indexed);
|
||||
|
||||
private:
|
||||
TextureCache& texture_cache;
|
||||
BufferCache& buffer_cache;
|
||||
Tegra::MemoryManager& gpu_memory;
|
||||
Tegra::Engines::Maxwell3D& maxwell3d;
|
||||
ProgramManager& program_manager;
|
||||
StateTracker& state_tracker;
|
||||
|
||||
OGLProgram program;
|
||||
std::array<Shader::Info, 5> stage_infos{};
|
||||
std::array<u32, 5> base_uniform_bindings{};
|
||||
std::array<u32, 5> base_storage_bindings{};
|
||||
std::array<u32, 5> num_texture_buffers{};
|
||||
std::array<u32, 5> num_image_buffers{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<OpenGL::GraphicsProgramKey> {
|
||||
size_t operator()(const OpenGL::GraphicsProgramKey& k) const noexcept {
|
||||
return k.Hash();
|
||||
}
|
||||
};
|
||||
} // namespace std
|
Loading…
Reference in New Issue