2019-04-10 20:56:12 +02:00
|
|
|
// Copyright 2019 yuzu Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2019-09-18 06:07:01 +02:00
|
|
|
#include <algorithm>
|
2019-05-24 01:19:56 +02:00
|
|
|
#include <array>
|
2019-04-10 20:56:12 +02:00
|
|
|
#include <cstddef>
|
2019-09-18 06:07:01 +02:00
|
|
|
#include <vector>
|
2019-04-10 20:56:12 +02:00
|
|
|
#include <glad/glad.h>
|
|
|
|
|
2019-04-10 23:03:52 +02:00
|
|
|
#include "common/logging/log.h"
|
2019-05-24 01:19:56 +02:00
|
|
|
#include "common/scope_exit.h"
|
2019-04-10 20:56:12 +02:00
|
|
|
#include "video_core/renderer_opengl/gl_device.h"
|
2019-05-24 01:19:56 +02:00
|
|
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
2019-04-10 20:56:12 +02:00
|
|
|
|
|
|
|
namespace OpenGL {
|
|
|
|
|
|
|
|
namespace {
|
2019-08-31 08:03:35 +02:00
|
|
|
|
2019-11-19 01:38:15 +01:00
|
|
|
// One uniform block is reserved for emulation purposes
|
|
|
|
constexpr u32 ReservedUniformBlocks = 1;
|
|
|
|
|
2019-04-10 20:56:12 +02:00
|
|
|
template <typename T>
|
|
|
|
T GetInteger(GLenum pname) {
|
|
|
|
GLint temporary;
|
|
|
|
glGetIntegerv(pname, &temporary);
|
|
|
|
return static_cast<T>(temporary);
|
|
|
|
}
|
2019-08-31 08:03:35 +02:00
|
|
|
|
|
|
|
bool TestProgram(const GLchar* glsl) {
|
|
|
|
const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &glsl)};
|
|
|
|
GLint link_status;
|
|
|
|
glGetProgramiv(shader, GL_LINK_STATUS, &link_status);
|
|
|
|
glDeleteProgram(shader);
|
|
|
|
return link_status == GL_TRUE;
|
|
|
|
}
|
|
|
|
|
2019-09-18 06:07:01 +02:00
|
|
|
std::vector<std::string_view> GetExtensions() {
|
|
|
|
GLint num_extensions;
|
|
|
|
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
|
|
|
|
std::vector<std::string_view> extensions;
|
|
|
|
extensions.reserve(num_extensions);
|
|
|
|
for (GLint index = 0; index < num_extensions; ++index) {
|
|
|
|
extensions.push_back(
|
|
|
|
reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, static_cast<GLuint>(index))));
|
|
|
|
}
|
|
|
|
return extensions;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HasExtension(const std::vector<std::string_view>& images, std::string_view extension) {
|
|
|
|
return std::find(images.begin(), images.end(), extension) != images.end();
|
|
|
|
}
|
|
|
|
|
2019-11-19 01:38:15 +01:00
|
|
|
constexpr Device::BaseBindings operator+(Device::BaseBindings lhs, Device::BaseBindings rhs) {
|
|
|
|
return Device::BaseBindings{lhs.uniform_buffer + rhs.uniform_buffer,
|
|
|
|
lhs.shader_storage_buffer + rhs.shader_storage_buffer,
|
|
|
|
lhs.sampler + rhs.sampler, lhs.image + rhs.image};
|
|
|
|
}
|
|
|
|
|
|
|
|
Device::BaseBindings BuildBaseBindings(GLenum uniform_blocks, GLenum shader_storage_blocks,
|
|
|
|
GLenum texture_image_units, GLenum image_uniforms) noexcept {
|
|
|
|
return Device::BaseBindings{
|
|
|
|
GetInteger<u32>(uniform_blocks) - ReservedUniformBlocks,
|
|
|
|
GetInteger<u32>(shader_storage_blocks),
|
|
|
|
GetInteger<u32>(texture_image_units),
|
|
|
|
GetInteger<u32>(image_uniforms),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-04-10 20:56:12 +02:00
|
|
|
} // Anonymous namespace
|
|
|
|
|
2019-04-10 23:03:52 +02:00
|
|
|
Device::Device() {
|
2019-11-02 08:08:31 +01:00
|
|
|
const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
2019-09-18 06:07:01 +02:00
|
|
|
const std::vector extensions = GetExtensions();
|
|
|
|
|
2019-11-02 08:08:31 +01:00
|
|
|
const bool is_nvidia = vendor == "NVIDIA Corporation";
|
|
|
|
|
2019-11-19 01:38:15 +01:00
|
|
|
// Reserve the first UBO for emulation bindings
|
|
|
|
base_bindings[0] = BaseBindings{ReservedUniformBlocks, 0, 0, 0};
|
|
|
|
base_bindings[1] = base_bindings[0] + BuildBaseBindings(GL_MAX_VERTEX_UNIFORM_BLOCKS,
|
|
|
|
GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS,
|
|
|
|
GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
|
|
|
|
GL_MAX_VERTEX_IMAGE_UNIFORMS);
|
|
|
|
base_bindings[2] =
|
|
|
|
base_bindings[1] + BuildBaseBindings(GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS,
|
|
|
|
GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS,
|
|
|
|
GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS,
|
|
|
|
GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS);
|
|
|
|
base_bindings[3] =
|
|
|
|
base_bindings[2] + BuildBaseBindings(GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS,
|
|
|
|
GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS,
|
|
|
|
GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS,
|
|
|
|
GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS);
|
|
|
|
base_bindings[4] = base_bindings[3] + BuildBaseBindings(GL_MAX_GEOMETRY_UNIFORM_BLOCKS,
|
|
|
|
GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS,
|
|
|
|
GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS,
|
|
|
|
GL_MAX_GEOMETRY_IMAGE_UNIFORMS);
|
|
|
|
// Compute doesn't need any of that
|
|
|
|
base_bindings[5] = BaseBindings{0, 0, 0, 0};
|
|
|
|
|
2019-04-10 20:56:12 +02:00
|
|
|
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
2019-06-01 22:41:55 +02:00
|
|
|
shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
|
2019-04-30 05:09:51 +02:00
|
|
|
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
|
|
|
|
max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
|
2019-08-10 04:50:21 +02:00
|
|
|
has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group &&
|
|
|
|
GLAD_GL_NV_shader_thread_shuffle;
|
2019-11-08 00:03:50 +01:00
|
|
|
has_shader_ballot = GLAD_GL_ARB_shader_ballot;
|
2019-07-08 01:36:42 +02:00
|
|
|
has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array;
|
2019-09-18 06:07:01 +02:00
|
|
|
has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted");
|
2019-04-10 23:03:52 +02:00
|
|
|
has_variable_aoffi = TestVariableAoffi();
|
2019-05-24 01:19:56 +02:00
|
|
|
has_component_indexing_bug = TestComponentIndexingBug();
|
2019-08-31 08:03:35 +02:00
|
|
|
has_precise_bug = TestPreciseBug();
|
2019-11-02 08:08:31 +01:00
|
|
|
has_fast_buffer_sub_data = is_nvidia;
|
2019-08-31 08:03:35 +02:00
|
|
|
|
|
|
|
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
|
|
|
|
LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
|
|
|
|
LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);
|
2019-04-10 23:03:52 +02:00
|
|
|
}
|
|
|
|
|
2019-04-30 05:09:51 +02:00
|
|
|
Device::Device(std::nullptr_t) {
|
|
|
|
uniform_buffer_alignment = 0;
|
|
|
|
max_vertex_attributes = 16;
|
|
|
|
max_varyings = 15;
|
2019-08-10 04:50:21 +02:00
|
|
|
has_warp_intrinsics = true;
|
2019-11-08 00:03:50 +01:00
|
|
|
has_shader_ballot = true;
|
2019-07-08 01:36:42 +02:00
|
|
|
has_vertex_viewport_layer = true;
|
2019-09-18 06:07:01 +02:00
|
|
|
has_image_load_formatted = true;
|
2019-04-30 05:09:51 +02:00
|
|
|
has_variable_aoffi = true;
|
2019-05-24 01:19:56 +02:00
|
|
|
has_component_indexing_bug = false;
|
2019-08-31 08:03:35 +02:00
|
|
|
has_precise_bug = false;
|
2019-04-30 05:09:51 +02:00
|
|
|
}
|
|
|
|
|
2019-04-10 23:03:52 +02:00
|
|
|
bool Device::TestVariableAoffi() {
|
2019-08-31 08:03:35 +02:00
|
|
|
return TestProgram(R"(#version 430 core
|
2019-05-27 05:52:49 +02:00
|
|
|
// This is a unit test, please ignore me on apitrace bug reports.
|
2019-04-10 23:03:52 +02:00
|
|
|
uniform sampler2D tex;
|
|
|
|
uniform ivec2 variable_offset;
|
2019-06-12 04:02:50 +02:00
|
|
|
out vec4 output_attribute;
|
2019-04-10 23:03:52 +02:00
|
|
|
void main() {
|
2019-06-12 04:02:50 +02:00
|
|
|
output_attribute = textureOffset(tex, vec2(0), variable_offset);
|
2019-08-31 08:03:35 +02:00
|
|
|
})");
|
2019-04-10 20:56:12 +02:00
|
|
|
}
|
|
|
|
|
2019-05-24 01:19:56 +02:00
|
|
|
bool Device::TestComponentIndexingBug() {
|
|
|
|
const GLchar* COMPONENT_TEST = R"(#version 430 core
|
|
|
|
layout (std430, binding = 0) buffer OutputBuffer {
|
|
|
|
uint output_value;
|
|
|
|
};
|
|
|
|
layout (std140, binding = 0) uniform InputBuffer {
|
|
|
|
uvec4 input_value[4096];
|
|
|
|
};
|
|
|
|
layout (location = 0) uniform uint idx;
|
|
|
|
void main() {
|
|
|
|
output_value = input_value[idx >> 2][idx & 3];
|
|
|
|
})";
|
|
|
|
const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &COMPONENT_TEST)};
|
|
|
|
SCOPE_EXIT({ glDeleteProgram(shader); });
|
|
|
|
glUseProgram(shader);
|
|
|
|
|
|
|
|
OGLVertexArray vao;
|
|
|
|
vao.Create();
|
|
|
|
glBindVertexArray(vao.handle);
|
|
|
|
|
|
|
|
constexpr std::array<GLuint, 8> values{0, 0, 0, 0, 0x1236327, 0x985482, 0x872753, 0x2378432};
|
|
|
|
OGLBuffer ubo;
|
|
|
|
ubo.Create();
|
|
|
|
glNamedBufferData(ubo.handle, sizeof(values), values.data(), GL_STATIC_DRAW);
|
|
|
|
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo.handle);
|
|
|
|
|
|
|
|
OGLBuffer ssbo;
|
|
|
|
ssbo.Create();
|
|
|
|
glNamedBufferStorage(ssbo.handle, sizeof(GLuint), nullptr, GL_CLIENT_STORAGE_BIT);
|
|
|
|
|
|
|
|
for (GLuint index = 4; index < 8; ++index) {
|
|
|
|
glInvalidateBufferData(ssbo.handle);
|
|
|
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo.handle);
|
|
|
|
|
|
|
|
glProgramUniform1ui(shader, 0, index);
|
|
|
|
glDrawArrays(GL_POINTS, 0, 1);
|
|
|
|
|
|
|
|
GLuint result;
|
|
|
|
glGetNamedBufferSubData(ssbo.handle, 0, sizeof(result), &result);
|
|
|
|
if (result != values.at(index)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-31 08:03:35 +02:00
|
|
|
bool Device::TestPreciseBug() {
|
|
|
|
return !TestProgram(R"(#version 430 core
|
|
|
|
in vec3 coords;
|
|
|
|
out float out_value;
|
|
|
|
uniform sampler2DShadow tex;
|
|
|
|
void main() {
|
|
|
|
precise float tmp_value = vec4(texture(tex, coords)).x;
|
|
|
|
out_value = tmp_value;
|
|
|
|
})");
|
|
|
|
}
|
|
|
|
|
2019-04-10 20:56:12 +02:00
|
|
|
} // namespace OpenGL
|