|
|
|
@ -153,16 +153,19 @@ void UpdateBindlessPointers(GLenum target, GLuint64EXT* pointers, std::size_t nu |
|
|
|
|
|
|
|
} // Anonymous namespace
|
|
|
|
|
|
|
|
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, |
|
|
|
const Device& device, ScreenInfo& info, |
|
|
|
ProgramManager& program_manager, StateTracker& state_tracker) |
|
|
|
: RasterizerAccelerated{system.Memory()}, device{device}, texture_cache{system, *this, device, |
|
|
|
state_tracker}, |
|
|
|
shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, |
|
|
|
buffer_cache{*this, system, device, STREAM_BUFFER_SIZE}, |
|
|
|
fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system}, |
|
|
|
screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker}, |
|
|
|
async_shaders{emu_window} { |
|
|
|
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_, |
|
|
|
Core::Memory::Memory& cpu_memory, const Device& device_, |
|
|
|
ScreenInfo& screen_info_, ProgramManager& program_manager_, |
|
|
|
StateTracker& state_tracker_) |
|
|
|
: RasterizerAccelerated{cpu_memory}, gpu(gpu_), maxwell3d(gpu.Maxwell3D()), |
|
|
|
kepler_compute(gpu.KeplerCompute()), gpu_memory(gpu.MemoryManager()), device(device_), |
|
|
|
screen_info(screen_info_), program_manager(program_manager_), state_tracker(state_tracker_), |
|
|
|
texture_cache(*this, maxwell3d, gpu_memory, device, state_tracker), |
|
|
|
shader_cache(*this, emu_window, gpu, maxwell3d, kepler_compute, gpu_memory, device), |
|
|
|
query_cache(*this, maxwell3d, gpu_memory), |
|
|
|
buffer_cache(*this, gpu_memory, cpu_memory, device, STREAM_BUFFER_SIZE), |
|
|
|
fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache), |
|
|
|
async_shaders(emu_window) { |
|
|
|
CheckExtensions(); |
|
|
|
|
|
|
|
unified_uniform_buffer.Create(); |
|
|
|
@ -196,8 +199,7 @@ void RasterizerOpenGL::CheckExtensions() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupVertexFormat() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::VertexFormats]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -217,7 +219,7 @@ void RasterizerOpenGL::SetupVertexFormat() { |
|
|
|
} |
|
|
|
flags[Dirty::VertexFormat0 + index] = false; |
|
|
|
|
|
|
|
const auto attrib = gpu.regs.vertex_attrib_format[index]; |
|
|
|
const auto attrib = maxwell3d.regs.vertex_attrib_format[index]; |
|
|
|
const auto gl_index = static_cast<GLuint>(index); |
|
|
|
|
|
|
|
// Disable constant attributes.
|
|
|
|
@ -241,8 +243,7 @@ void RasterizerOpenGL::SetupVertexFormat() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupVertexBuffer() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::VertexBuffers]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -253,7 +254,7 @@ void RasterizerOpenGL::SetupVertexBuffer() { |
|
|
|
const bool use_unified_memory = device.HasVertexBufferUnifiedMemory(); |
|
|
|
|
|
|
|
// Upload all guest vertex arrays sequentially to our buffer
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_BINDINGS; ++index) { |
|
|
|
if (!flags[Dirty::VertexBuffer0 + index]) { |
|
|
|
continue; |
|
|
|
@ -290,14 +291,13 @@ void RasterizerOpenGL::SetupVertexBuffer() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupVertexInstances() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::VertexInstances]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::VertexInstances] = false; |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) { |
|
|
|
if (!flags[Dirty::VertexInstance0 + index]) { |
|
|
|
continue; |
|
|
|
@ -313,7 +313,7 @@ void RasterizerOpenGL::SetupVertexInstances() { |
|
|
|
|
|
|
|
GLintptr RasterizerOpenGL::SetupIndexBuffer() { |
|
|
|
MICROPROFILE_SCOPE(OpenGL_Index); |
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
const std::size_t size = CalculateIndexBufferSize(); |
|
|
|
const auto info = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); |
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.handle); |
|
|
|
@ -322,15 +322,14 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() { |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { |
|
|
|
MICROPROFILE_SCOPE(OpenGL_Shader); |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
u32 clip_distances = 0; |
|
|
|
|
|
|
|
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { |
|
|
|
const auto& shader_config = gpu.regs.shader_config[index]; |
|
|
|
const auto& shader_config = maxwell3d.regs.shader_config[index]; |
|
|
|
const auto program{static_cast<Maxwell::ShaderProgram>(index)}; |
|
|
|
|
|
|
|
// Skip stages that are not enabled
|
|
|
|
if (!gpu.regs.IsShaderConfigEnabled(index)) { |
|
|
|
if (!maxwell3d.regs.IsShaderConfigEnabled(index)) { |
|
|
|
switch (program) { |
|
|
|
case Maxwell::ShaderProgram::Geometry: |
|
|
|
program_manager.UseGeometryShader(0); |
|
|
|
@ -391,11 +390,11 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { |
|
|
|
} |
|
|
|
|
|
|
|
SyncClipEnabled(clip_distances); |
|
|
|
gpu.dirty.flags[Dirty::Shaders] = false; |
|
|
|
maxwell3d.dirty.flags[Dirty::Shaders] = false; |
|
|
|
} |
|
|
|
|
|
|
|
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { |
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
|
|
|
|
std::size_t size = 0; |
|
|
|
for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { |
|
|
|
@ -413,34 +412,27 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { |
|
|
|
} |
|
|
|
|
|
|
|
std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const { |
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs; |
|
|
|
|
|
|
|
return static_cast<std::size_t>(regs.index_array.count) * |
|
|
|
static_cast<std::size_t>(regs.index_array.FormatSizeInBytes()); |
|
|
|
return static_cast<std::size_t>(maxwell3d.regs.index_array.count) * |
|
|
|
static_cast<std::size_t>(maxwell3d.regs.index_array.FormatSizeInBytes()); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading, |
|
|
|
void RasterizerOpenGL::LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading, |
|
|
|
const VideoCore::DiskResourceLoadCallback& callback) { |
|
|
|
shader_cache.LoadDiskCache(stop_loading, callback); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupDirtyFlags() { |
|
|
|
state_tracker.Initialize(); |
|
|
|
shader_cache.LoadDiskCache(title_id, stop_loading, callback); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::ConfigureFramebuffers() { |
|
|
|
MICROPROFILE_SCOPE(OpenGL_Framebuffer); |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
if (!gpu.dirty.flags[VideoCommon::Dirty::RenderTargets]) { |
|
|
|
if (!maxwell3d.dirty.flags[VideoCommon::Dirty::RenderTargets]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
gpu.dirty.flags[VideoCommon::Dirty::RenderTargets] = false; |
|
|
|
maxwell3d.dirty.flags[VideoCommon::Dirty::RenderTargets] = false; |
|
|
|
|
|
|
|
texture_cache.GuardRenderTargets(true); |
|
|
|
|
|
|
|
View depth_surface = texture_cache.GetDepthBufferSurface(true); |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0); |
|
|
|
|
|
|
|
// Bind the framebuffer surfaces
|
|
|
|
@ -472,8 +464,7 @@ void RasterizerOpenGL::ConfigureFramebuffers() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color, bool using_depth_stencil) { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
|
|
|
|
texture_cache.GuardRenderTargets(true); |
|
|
|
View color_surface; |
|
|
|
@ -523,12 +514,11 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color, bool using_de |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::Clear() { |
|
|
|
const auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
if (!gpu.ShouldExecute()) { |
|
|
|
if (!maxwell3d.ShouldExecute()) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
bool use_color{}; |
|
|
|
bool use_depth{}; |
|
|
|
bool use_stencil{}; |
|
|
|
@ -593,7 +583,6 @@ void RasterizerOpenGL::Clear() { |
|
|
|
|
|
|
|
void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
|
|
|
MICROPROFILE_SCOPE(OpenGL_Drawing); |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
|
|
|
|
query_cache.UpdateCounters(); |
|
|
|
|
|
|
|
@ -641,7 +630,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
|
|
|
|
|
|
|
if (invalidated) { |
|
|
|
// When the stream buffer has been invalidated, we have to consider vertex buffers as dirty
|
|
|
|
auto& dirty = gpu.dirty.flags; |
|
|
|
auto& dirty = maxwell3d.dirty.flags; |
|
|
|
dirty[Dirty::VertexBuffers] = true; |
|
|
|
for (int index = Dirty::VertexBuffer0; index <= Dirty::VertexBuffer31; ++index) { |
|
|
|
dirty[index] = true; |
|
|
|
@ -662,7 +651,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
|
|
|
// Setup emulation uniform buffer.
|
|
|
|
if (!device.UseAssemblyShaders()) { |
|
|
|
MaxwellUniformData ubo; |
|
|
|
ubo.SetFromRegs(gpu); |
|
|
|
ubo.SetFromRegs(maxwell3d); |
|
|
|
const auto info = |
|
|
|
buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); |
|
|
|
glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, info.handle, info.offset, |
|
|
|
@ -671,7 +660,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
|
|
|
|
|
|
|
// Setup shaders and their used resources.
|
|
|
|
texture_cache.GuardSamplers(true); |
|
|
|
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology); |
|
|
|
const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d.regs.draw.topology); |
|
|
|
SetupShaders(primitive_mode); |
|
|
|
texture_cache.GuardSamplers(false); |
|
|
|
|
|
|
|
@ -688,14 +677,14 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
|
|
|
|
|
|
|
BeginTransformFeedback(primitive_mode); |
|
|
|
|
|
|
|
const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance); |
|
|
|
const GLuint base_instance = static_cast<GLuint>(maxwell3d.regs.vb_base_instance); |
|
|
|
const GLsizei num_instances = |
|
|
|
static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1); |
|
|
|
static_cast<GLsizei>(is_instanced ? maxwell3d.mme_draw.instance_count : 1); |
|
|
|
if (is_indexed) { |
|
|
|
const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base); |
|
|
|
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count); |
|
|
|
const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vb_element_base); |
|
|
|
const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.index_array.count); |
|
|
|
const GLvoid* offset = reinterpret_cast<const GLvoid*>(index_buffer_offset); |
|
|
|
const GLenum format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format); |
|
|
|
const GLenum format = MaxwellToGL::IndexFormat(maxwell3d.regs.index_array.format); |
|
|
|
if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { |
|
|
|
glDrawElements(primitive_mode, num_vertices, format, offset); |
|
|
|
} else if (num_instances == 1 && base_instance == 0) { |
|
|
|
@ -714,8 +703,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
|
|
|
base_instance); |
|
|
|
} |
|
|
|
} else { |
|
|
|
const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first); |
|
|
|
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count); |
|
|
|
const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vertex_buffer.first); |
|
|
|
const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.vertex_buffer.count); |
|
|
|
if (num_instances == 1 && base_instance == 0) { |
|
|
|
glDrawArrays(primitive_mode, base_vertex, num_vertices); |
|
|
|
} else if (base_instance == 0) { |
|
|
|
@ -730,7 +719,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
|
|
|
|
|
|
|
++num_queued_commands; |
|
|
|
|
|
|
|
system.GPU().TickWork(); |
|
|
|
gpu.TickWork(); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { |
|
|
|
@ -753,7 +742,8 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { |
|
|
|
|
|
|
|
buffer_cache.Unmap(); |
|
|
|
|
|
|
|
const auto& launch_desc = system.GPU().KeplerCompute().launch_description; |
|
|
|
const auto& launch_desc = kepler_compute.launch_description; |
|
|
|
program_manager.BindCompute(kernel->GetHandle()); |
|
|
|
glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z); |
|
|
|
++num_queued_commands; |
|
|
|
} |
|
|
|
@ -815,17 +805,14 @@ void RasterizerOpenGL::SyncGuestHost() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) { |
|
|
|
auto& gpu{system.GPU()}; |
|
|
|
if (!gpu.IsAsync()) { |
|
|
|
auto& memory_manager{gpu.MemoryManager()}; |
|
|
|
memory_manager.Write<u32>(addr, value); |
|
|
|
gpu_memory.Write<u32>(addr, value); |
|
|
|
return; |
|
|
|
} |
|
|
|
fence_manager.SignalSemaphore(addr, value); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SignalSyncPoint(u32 value) { |
|
|
|
auto& gpu{system.GPU()}; |
|
|
|
if (!gpu.IsAsync()) { |
|
|
|
gpu.IncrementSyncPoint(value); |
|
|
|
return; |
|
|
|
@ -834,7 +821,6 @@ void RasterizerOpenGL::SignalSyncPoint(u32 value) { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::ReleaseFences() { |
|
|
|
auto& gpu{system.GPU()}; |
|
|
|
if (!gpu.IsAsync()) { |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -920,7 +906,7 @@ void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, Shader* sh |
|
|
|
GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV}; |
|
|
|
|
|
|
|
MICROPROFILE_SCOPE(OpenGL_UBO); |
|
|
|
const auto& stages = system.GPU().Maxwell3D().state.shader_stages; |
|
|
|
const auto& stages = maxwell3d.state.shader_stages; |
|
|
|
const auto& shader_stage = stages[stage_index]; |
|
|
|
const auto& entries = shader->GetEntries(); |
|
|
|
const bool use_unified = entries.use_unified_uniforms; |
|
|
|
@ -945,7 +931,7 @@ void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, Shader* sh |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupComputeConstBuffers(Shader* kernel) { |
|
|
|
MICROPROFILE_SCOPE(OpenGL_UBO); |
|
|
|
const auto& launch_desc = system.GPU().KeplerCompute().launch_description; |
|
|
|
const auto& launch_desc = kepler_compute.launch_description; |
|
|
|
const auto& entries = kernel->GetEntries(); |
|
|
|
const bool use_unified = entries.use_unified_uniforms; |
|
|
|
|
|
|
|
@ -1018,9 +1004,7 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* sh |
|
|
|
GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV, |
|
|
|
}; |
|
|
|
|
|
|
|
auto& gpu{system.GPU()}; |
|
|
|
auto& memory_manager{gpu.MemoryManager()}; |
|
|
|
const auto& cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]}; |
|
|
|
const auto& cbufs{maxwell3d.state.shader_stages[stage_index]}; |
|
|
|
const auto& entries{shader->GetEntries().global_memory_entries}; |
|
|
|
|
|
|
|
std::array<GLuint64EXT, 32> pointers; |
|
|
|
@ -1030,8 +1014,8 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* sh |
|
|
|
u32 binding = assembly_shaders ? 0 : device.GetBaseBindings(stage_index).shader_storage_buffer; |
|
|
|
for (const auto& entry : entries) { |
|
|
|
const GPUVAddr addr{cbufs.const_buffers[entry.cbuf_index].address + entry.cbuf_offset}; |
|
|
|
const GPUVAddr gpu_addr{memory_manager.Read<u64>(addr)}; |
|
|
|
const u32 size{memory_manager.Read<u32>(addr + 8)}; |
|
|
|
const GPUVAddr gpu_addr{gpu_memory.Read<u64>(addr)}; |
|
|
|
const u32 size{gpu_memory.Read<u32>(addr + 8)}; |
|
|
|
SetupGlobalMemory(binding, entry, gpu_addr, size, &pointers[binding]); |
|
|
|
++binding; |
|
|
|
} |
|
|
|
@ -1041,9 +1025,7 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* sh |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) { |
|
|
|
auto& gpu{system.GPU()}; |
|
|
|
auto& memory_manager{gpu.MemoryManager()}; |
|
|
|
const auto& cbufs{gpu.KeplerCompute().launch_description.const_buffer_config}; |
|
|
|
const auto& cbufs{kepler_compute.launch_description.const_buffer_config}; |
|
|
|
const auto& entries{kernel->GetEntries().global_memory_entries}; |
|
|
|
|
|
|
|
std::array<GLuint64EXT, 32> pointers; |
|
|
|
@ -1052,8 +1034,8 @@ void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) { |
|
|
|
u32 binding = 0; |
|
|
|
for (const auto& entry : entries) { |
|
|
|
const GPUVAddr addr{cbufs[entry.cbuf_index].Address() + entry.cbuf_offset}; |
|
|
|
const GPUVAddr gpu_addr{memory_manager.Read<u64>(addr)}; |
|
|
|
const u32 size{memory_manager.Read<u32>(addr + 8)}; |
|
|
|
const GPUVAddr gpu_addr{gpu_memory.Read<u64>(addr)}; |
|
|
|
const u32 size{gpu_memory.Read<u32>(addr + 8)}; |
|
|
|
SetupGlobalMemory(binding, entry, gpu_addr, size, &pointers[binding]); |
|
|
|
++binding; |
|
|
|
} |
|
|
|
@ -1077,7 +1059,6 @@ void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& e |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader) { |
|
|
|
MICROPROFILE_SCOPE(OpenGL_Texture); |
|
|
|
const auto& maxwell3d = system.GPU().Maxwell3D(); |
|
|
|
u32 binding = device.GetBaseBindings(stage_index).sampler; |
|
|
|
for (const auto& entry : shader->GetEntries().samplers) { |
|
|
|
const auto shader_type = static_cast<ShaderType>(stage_index); |
|
|
|
@ -1090,11 +1071,10 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupComputeTextures(Shader* kernel) { |
|
|
|
MICROPROFILE_SCOPE(OpenGL_Texture); |
|
|
|
const auto& compute = system.GPU().KeplerCompute(); |
|
|
|
u32 binding = 0; |
|
|
|
for (const auto& entry : kernel->GetEntries().samplers) { |
|
|
|
for (std::size_t i = 0; i < entry.size; ++i) { |
|
|
|
const auto texture = GetTextureInfo(compute, entry, ShaderType::Compute, i); |
|
|
|
const auto texture = GetTextureInfo(kepler_compute, entry, ShaderType::Compute, i); |
|
|
|
SetupTexture(binding++, texture, entry); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -1118,20 +1098,18 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, Shader* shader) { |
|
|
|
const auto& maxwell3d = system.GPU().Maxwell3D(); |
|
|
|
u32 binding = device.GetBaseBindings(stage_index).image; |
|
|
|
for (const auto& entry : shader->GetEntries().images) { |
|
|
|
const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); |
|
|
|
const auto shader_type = static_cast<ShaderType>(stage_index); |
|
|
|
const auto tic = GetTextureInfo(maxwell3d, entry, shader_type).tic; |
|
|
|
SetupImage(binding++, tic, entry); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SetupComputeImages(Shader* shader) { |
|
|
|
const auto& compute = system.GPU().KeplerCompute(); |
|
|
|
u32 binding = 0; |
|
|
|
for (const auto& entry : shader->GetEntries().images) { |
|
|
|
const auto tic = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute).tic; |
|
|
|
const auto tic = GetTextureInfo(kepler_compute, entry, ShaderType::Compute).tic; |
|
|
|
SetupImage(binding++, tic, entry); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -1151,9 +1129,8 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncViewport() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
const auto& regs = gpu.regs; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
|
|
|
|
const bool dirty_viewport = flags[Dirty::Viewports]; |
|
|
|
const bool dirty_clip_control = flags[Dirty::ClipControl]; |
|
|
|
@ -1225,25 +1202,23 @@ void RasterizerOpenGL::SyncViewport() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncDepthClamp() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::DepthClampEnabled]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::DepthClampEnabled] = false; |
|
|
|
|
|
|
|
oglEnable(GL_DEPTH_CLAMP, gpu.regs.view_volume_clip_control.depth_clamp_disabled == 0); |
|
|
|
oglEnable(GL_DEPTH_CLAMP, maxwell3d.regs.view_volume_clip_control.depth_clamp_disabled == 0); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::ClipDistances] && !flags[Dirty::Shaders]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::ClipDistances] = false; |
|
|
|
|
|
|
|
clip_mask &= gpu.regs.clip_distance_enabled; |
|
|
|
clip_mask &= maxwell3d.regs.clip_distance_enabled; |
|
|
|
if (clip_mask == last_clip_distance_mask) { |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -1259,9 +1234,8 @@ void RasterizerOpenGL::SyncClipCoef() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncCullMode() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
const auto& regs = gpu.regs; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
|
|
|
|
if (flags[Dirty::CullTest]) { |
|
|
|
flags[Dirty::CullTest] = false; |
|
|
|
@ -1276,26 +1250,24 @@ void RasterizerOpenGL::SyncCullMode() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncPrimitiveRestart() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::PrimitiveRestart]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::PrimitiveRestart] = false; |
|
|
|
|
|
|
|
if (gpu.regs.primitive_restart.enabled) { |
|
|
|
if (maxwell3d.regs.primitive_restart.enabled) { |
|
|
|
glEnable(GL_PRIMITIVE_RESTART); |
|
|
|
glPrimitiveRestartIndex(gpu.regs.primitive_restart.index); |
|
|
|
glPrimitiveRestartIndex(maxwell3d.regs.primitive_restart.index); |
|
|
|
} else { |
|
|
|
glDisable(GL_PRIMITIVE_RESTART); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncDepthTestState() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
if (flags[Dirty::DepthMask]) { |
|
|
|
flags[Dirty::DepthMask] = false; |
|
|
|
glDepthMask(regs.depth_write_enabled ? GL_TRUE : GL_FALSE); |
|
|
|
@ -1313,14 +1285,13 @@ void RasterizerOpenGL::SyncDepthTestState() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncStencilTestState() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::StencilTest]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::StencilTest] = false; |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
oglEnable(GL_STENCIL_TEST, regs.stencil_enable); |
|
|
|
|
|
|
|
glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), |
|
|
|
@ -1345,25 +1316,24 @@ void RasterizerOpenGL::SyncStencilTestState() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncRasterizeEnable() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::RasterizeEnable]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::RasterizeEnable] = false; |
|
|
|
|
|
|
|
oglEnable(GL_RASTERIZER_DISCARD, gpu.regs.rasterize_enable == 0); |
|
|
|
oglEnable(GL_RASTERIZER_DISCARD, maxwell3d.regs.rasterize_enable == 0); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncPolygonModes() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::PolygonModes]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::PolygonModes] = false; |
|
|
|
|
|
|
|
if (gpu.regs.fill_rectangle) { |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
if (regs.fill_rectangle) { |
|
|
|
if (!GLAD_GL_NV_fill_rectangle) { |
|
|
|
LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported"); |
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); |
|
|
|
@ -1376,27 +1346,26 @@ void RasterizerOpenGL::SyncPolygonModes() { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (gpu.regs.polygon_mode_front == gpu.regs.polygon_mode_back) { |
|
|
|
if (regs.polygon_mode_front == regs.polygon_mode_back) { |
|
|
|
flags[Dirty::PolygonModeFront] = false; |
|
|
|
flags[Dirty::PolygonModeBack] = false; |
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front)); |
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, MaxwellToGL::PolygonMode(regs.polygon_mode_front)); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (flags[Dirty::PolygonModeFront]) { |
|
|
|
flags[Dirty::PolygonModeFront] = false; |
|
|
|
glPolygonMode(GL_FRONT, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front)); |
|
|
|
glPolygonMode(GL_FRONT, MaxwellToGL::PolygonMode(regs.polygon_mode_front)); |
|
|
|
} |
|
|
|
|
|
|
|
if (flags[Dirty::PolygonModeBack]) { |
|
|
|
flags[Dirty::PolygonModeBack] = false; |
|
|
|
glPolygonMode(GL_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_back)); |
|
|
|
glPolygonMode(GL_BACK, MaxwellToGL::PolygonMode(regs.polygon_mode_back)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncColorMask() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::ColorMasks]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -1405,7 +1374,7 @@ void RasterizerOpenGL::SyncColorMask() { |
|
|
|
const bool force = flags[Dirty::ColorMaskCommon]; |
|
|
|
flags[Dirty::ColorMaskCommon] = false; |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
if (regs.color_mask_common) { |
|
|
|
if (!force && !flags[Dirty::ColorMask0]) { |
|
|
|
return; |
|
|
|
@ -1430,33 +1399,30 @@ void RasterizerOpenGL::SyncColorMask() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncMultiSampleState() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::MultisampleControl]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::MultisampleControl] = false; |
|
|
|
|
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage); |
|
|
|
oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncFragmentColorClampState() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::FragmentClampColor]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::FragmentClampColor] = false; |
|
|
|
|
|
|
|
glClampColor(GL_CLAMP_FRAGMENT_COLOR, gpu.regs.frag_color_clamp ? GL_TRUE : GL_FALSE); |
|
|
|
glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d.regs.frag_color_clamp ? GL_TRUE : GL_FALSE); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncBlendState() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
const auto& regs = gpu.regs; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
|
|
|
|
if (flags[Dirty::BlendColor]) { |
|
|
|
flags[Dirty::BlendColor] = false; |
|
|
|
@ -1513,14 +1479,13 @@ void RasterizerOpenGL::SyncBlendState() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncLogicOpState() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::LogicOp]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::LogicOp] = false; |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
if (regs.logic_op.enable) { |
|
|
|
glEnable(GL_COLOR_LOGIC_OP); |
|
|
|
glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation)); |
|
|
|
@ -1530,14 +1495,13 @@ void RasterizerOpenGL::SyncLogicOpState() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncScissorTest() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::Scissors]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::Scissors] = false; |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
for (std::size_t index = 0; index < Maxwell::NumViewports; ++index) { |
|
|
|
if (!flags[Dirty::Scissor0 + index]) { |
|
|
|
continue; |
|
|
|
@ -1556,16 +1520,15 @@ void RasterizerOpenGL::SyncScissorTest() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncPointState() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::PointSize]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::PointSize] = false; |
|
|
|
|
|
|
|
oglEnable(GL_POINT_SPRITE, gpu.regs.point_sprite_enable); |
|
|
|
oglEnable(GL_POINT_SPRITE, maxwell3d.regs.point_sprite_enable); |
|
|
|
|
|
|
|
if (gpu.regs.vp_point_size.enable) { |
|
|
|
if (maxwell3d.regs.vp_point_size.enable) { |
|
|
|
// By definition of GL_POINT_SIZE, it only matters if GL_PROGRAM_POINT_SIZE is disabled.
|
|
|
|
glEnable(GL_PROGRAM_POINT_SIZE); |
|
|
|
return; |
|
|
|
@ -1573,32 +1536,30 @@ void RasterizerOpenGL::SyncPointState() { |
|
|
|
|
|
|
|
// Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
|
|
|
|
// in OpenGL).
|
|
|
|
glPointSize(std::max(1.0f, gpu.regs.point_size)); |
|
|
|
glPointSize(std::max(1.0f, maxwell3d.regs.point_size)); |
|
|
|
glDisable(GL_PROGRAM_POINT_SIZE); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncLineState() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::LineWidth]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::LineWidth] = false; |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable); |
|
|
|
glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncPolygonOffset() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::PolygonOffset]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::PolygonOffset] = false; |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable); |
|
|
|
oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable); |
|
|
|
oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable); |
|
|
|
@ -1612,14 +1573,13 @@ void RasterizerOpenGL::SyncPolygonOffset() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncAlphaTest() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::AlphaTest]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::AlphaTest] = false; |
|
|
|
|
|
|
|
const auto& regs = gpu.regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
if (regs.alpha_test_enabled && regs.rt_control.count > 1) { |
|
|
|
LOG_WARNING(Render_OpenGL, "Alpha testing with more than one render target is not tested"); |
|
|
|
} |
|
|
|
@ -1633,20 +1593,19 @@ void RasterizerOpenGL::SyncAlphaTest() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncFramebufferSRGB() { |
|
|
|
auto& gpu = system.GPU().Maxwell3D(); |
|
|
|
auto& flags = gpu.dirty.flags; |
|
|
|
auto& flags = maxwell3d.dirty.flags; |
|
|
|
if (!flags[Dirty::FramebufferSRGB]) { |
|
|
|
return; |
|
|
|
} |
|
|
|
flags[Dirty::FramebufferSRGB] = false; |
|
|
|
|
|
|
|
oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb); |
|
|
|
oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d.regs.framebuffer_srgb); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::SyncTransformFeedback() { |
|
|
|
// TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
|
|
|
|
// when this is required.
|
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
|
|
|
|
static constexpr std::size_t STRIDE = 3; |
|
|
|
std::array<GLint, 128 * STRIDE * Maxwell::NumTransformFeedbackBuffers> attribs; |
|
|
|
@ -1698,7 +1657,7 @@ void RasterizerOpenGL::SyncTransformFeedback() { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) { |
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
if (regs.tfb_enabled == 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -1741,7 +1700,7 @@ void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) { |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerOpenGL::EndTransformFeedback() { |
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs; |
|
|
|
const auto& regs = maxwell3d.regs; |
|
|
|
if (regs.tfb_enabled == 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|