|
@@ -93,6 +93,34 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
|
|
|
return buffer.size;
|
|
return buffer.size;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/// Translates hardware transform feedback indices
|
|
|
|
|
+/// @param location Hardware location
|
|
|
|
|
+/// @return Pair of ARB_transform_feedback3 token stream first and third arguments
|
|
|
|
|
+/// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt
|
|
|
|
|
+std::pair<GLint, GLint> TransformFeedbackEnum(u8 location) {
|
|
|
|
|
+ const u8 index = location / 4;
|
|
|
|
|
+ if (index >= 8 && index <= 39) {
|
|
|
|
|
+ return {GL_GENERIC_ATTRIB_NV, index - 8};
|
|
|
|
|
+ }
|
|
|
|
|
+ if (index >= 48 && index <= 55) {
|
|
|
|
|
+ return {GL_TEXTURE_COORD_NV, index - 48};
|
|
|
|
|
+ }
|
|
|
|
|
+ switch (index) {
|
|
|
|
|
+ case 7:
|
|
|
|
|
+ return {GL_POSITION, 0};
|
|
|
|
|
+ case 40:
|
|
|
|
|
+ return {GL_PRIMARY_COLOR_NV, 0};
|
|
|
|
|
+ case 41:
|
|
|
|
|
+ return {GL_SECONDARY_COLOR_NV, 0};
|
|
|
|
|
+ case 42:
|
|
|
|
|
+ return {GL_BACK_PRIMARY_COLOR_NV, 0};
|
|
|
|
|
+ case 43:
|
|
|
|
|
+ return {GL_BACK_SECONDARY_COLOR_NV, 0};
|
|
|
|
|
+ }
|
|
|
|
|
+ UNIMPLEMENTED_MSG("index={}", static_cast<int>(index));
|
|
|
|
|
+ return {GL_POSITION, 0};
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void oglEnable(GLenum cap, bool state) {
|
|
void oglEnable(GLenum cap, bool state) {
|
|
|
(state ? glEnable : glDisable)(cap);
|
|
(state ? glEnable : glDisable)(cap);
|
|
|
}
|
|
}
|
|
@@ -1547,12 +1575,70 @@ void RasterizerOpenGL::SyncFramebufferSRGB() {
|
|
|
oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb);
|
|
oglEnable(GL_FRAMEBUFFER_SRGB, gpu.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;
|
|
|
|
|
+
|
|
|
|
|
+ static constexpr std::size_t STRIDE = 3;
|
|
|
|
|
+ std::array<GLint, 128 * STRIDE * Maxwell::NumTransformFeedbackBuffers> attribs;
|
|
|
|
|
+ std::array<GLint, Maxwell::NumTransformFeedbackBuffers> streams;
|
|
|
|
|
+
|
|
|
|
|
+ GLint* cursor = attribs.data();
|
|
|
|
|
+ GLint* current_stream = streams.data();
|
|
|
|
|
+
|
|
|
|
|
+ for (std::size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
|
|
|
|
|
+ const auto& layout = regs.tfb_layouts[feedback];
|
|
|
|
|
+ UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding");
|
|
|
|
|
+ if (layout.varying_count == 0) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ *current_stream = static_cast<GLint>(feedback);
|
|
|
|
|
+ if (current_stream != streams.data()) {
|
|
|
|
|
+ // When stepping one stream, push the expected token
|
|
|
|
|
+ cursor[0] = GL_NEXT_BUFFER_NV;
|
|
|
|
|
+ cursor[1] = 0;
|
|
|
|
|
+ cursor[2] = 0;
|
|
|
|
|
+ cursor += STRIDE;
|
|
|
|
|
+ }
|
|
|
|
|
+ ++current_stream;
|
|
|
|
|
+
|
|
|
|
|
+ const auto& locations = regs.tfb_varying_locs[feedback];
|
|
|
|
|
+ std::optional<u8> current_index;
|
|
|
|
|
+ for (u32 offset = 0; offset < layout.varying_count; ++offset) {
|
|
|
|
|
+ const u8 location = locations[offset];
|
|
|
|
|
+ const u8 index = location / 4;
|
|
|
|
|
+
|
|
|
|
|
+ if (current_index == index) {
|
|
|
|
|
+ // Increase number of components of the previous attachment
|
|
|
|
|
+ ++cursor[-2];
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ current_index = index;
|
|
|
|
|
+
|
|
|
|
|
+ std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location);
|
|
|
|
|
+ cursor[1] = 1;
|
|
|
|
|
+ cursor += STRIDE;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const GLsizei num_attribs = static_cast<GLsizei>((cursor - attribs.data()) / STRIDE);
|
|
|
|
|
+ const GLsizei num_strides = static_cast<GLsizei>(current_stream - streams.data());
|
|
|
|
|
+ glTransformFeedbackStreamAttribsNV(num_attribs, attribs.data(), num_strides, streams.data(),
|
|
|
|
|
+ GL_INTERLEAVED_ATTRIBS);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
|
|
void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
|
|
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
|
const auto& regs = system.GPU().Maxwell3D().regs;
|
|
|
if (regs.tfb_enabled == 0) {
|
|
if (regs.tfb_enabled == 0) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (device.UseAssemblyShaders()) {
|
|
|
|
|
+ SyncTransformFeedback();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
|
|
UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
|
|
|
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
|
|
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
|
|
|
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry));
|
|
regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry));
|
|
@@ -1579,6 +1665,10 @@ void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
|
|
|
static_cast<GLsizeiptr>(size));
|
|
static_cast<GLsizeiptr>(size));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // We may have to call BeginTransformFeedbackNV here since they seem to call different
|
|
|
|
|
+ // implementations on Nvidia's driver (the pointer is different) but we are using
|
|
|
|
|
+ // ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB
|
|
|
|
|
+ // extension doesn't define BeginTransformFeedback (without NV) interactions. It just works.
|
|
|
glBeginTransformFeedback(GL_POINTS);
|
|
glBeginTransformFeedback(GL_POINTS);
|
|
|
}
|
|
}
|
|
|
|
|
|