summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-05-21 18:17:53 -0300
committerGravatar ameerj2021-07-22 21:51:33 -0400
commit6bc54e12a0d274beee0cb7584f73429112ec98b2 (patch)
tree76d875eebeb3de8380a6071366e92995d1b1df80 /src
parentvideo_core: Abstract transform feedback translation utility (diff)
downloadyuzu-6bc54e12a0d274beee0cb7584f73429112ec98b2.tar.gz
yuzu-6bc54e12a0d274beee0cb7584f73429112ec98b2.tar.xz
yuzu-6bc54e12a0d274beee0cb7584f73429112ec98b2.zip
glasm: Set transform feedback state
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_program.cpp90
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_program.h32
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp98
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp19
5 files changed, 132 insertions, 113 deletions
diff --git a/src/video_core/renderer_opengl/gl_graphics_program.cpp b/src/video_core/renderer_opengl/gl_graphics_program.cpp
index b5d75aa13..9677a3ed6 100644
--- a/src/video_core/renderer_opengl/gl_graphics_program.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_program.cpp
@@ -12,7 +12,7 @@
12#include "video_core/texture_cache/texture_cache.h" 12#include "video_core/texture_cache/texture_cache.h"
13 13
14namespace OpenGL { 14namespace OpenGL {
15 15namespace {
16using Shader::ImageBufferDescriptor; 16using Shader::ImageBufferDescriptor;
17using Tegra::Texture::TexturePair; 17using Tegra::Texture::TexturePair;
18using VideoCommon::ImageId; 18using VideoCommon::ImageId;
@@ -20,6 +20,35 @@ using VideoCommon::ImageId;
20constexpr u32 MAX_TEXTURES = 64; 20constexpr u32 MAX_TEXTURES = 64;
21constexpr u32 MAX_IMAGES = 8; 21constexpr u32 MAX_IMAGES = 8;
22 22
23/// Translates hardware transform feedback indices
24/// @param location Hardware location
25/// @return Pair of ARB_transform_feedback3 token stream first and third arguments
26/// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt
27std::pair<GLint, GLint> TransformFeedbackEnum(u8 location) {
28 const u8 index = location / 4;
29 if (index >= 8 && index <= 39) {
30 return {GL_GENERIC_ATTRIB_NV, index - 8};
31 }
32 if (index >= 48 && index <= 55) {
33 return {GL_TEXTURE_COORD_NV, index - 48};
34 }
35 switch (index) {
36 case 7:
37 return {GL_POSITION, 0};
38 case 40:
39 return {GL_PRIMARY_COLOR_NV, 0};
40 case 41:
41 return {GL_SECONDARY_COLOR_NV, 0};
42 case 42:
43 return {GL_BACK_PRIMARY_COLOR_NV, 0};
44 case 43:
45 return {GL_BACK_SECONDARY_COLOR_NV, 0};
46 }
47 UNIMPLEMENTED_MSG("index={}", index);
48 return {GL_POSITION, 0};
49}
50} // Anonymous namespace
51
23size_t GraphicsProgramKey::Hash() const noexcept { 52size_t GraphicsProgramKey::Hash() const noexcept {
24 return static_cast<size_t>(Common::CityHash64(reinterpret_cast<const char*>(this), Size())); 53 return static_cast<size_t>(Common::CityHash64(reinterpret_cast<const char*>(this), Size()));
25} 54}
@@ -34,7 +63,8 @@ GraphicsProgram::GraphicsProgram(TextureCache& texture_cache_, BufferCache& buff
34 ProgramManager& program_manager_, StateTracker& state_tracker_, 63 ProgramManager& program_manager_, StateTracker& state_tracker_,
35 OGLProgram program_, 64 OGLProgram program_,
36 std::array<OGLAssemblyProgram, 5> assembly_programs_, 65 std::array<OGLAssemblyProgram, 5> assembly_programs_,
37 const std::array<const Shader::Info*, 5>& infos) 66 const std::array<const Shader::Info*, 5>& infos,
67 const VideoCommon::TransformFeedbackState* xfb_state)
38 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, 68 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
39 gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, program_manager{program_manager_}, 69 gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, program_manager{program_manager_},
40 state_tracker{state_tracker_}, program{std::move(program_)}, assembly_programs{std::move( 70 state_tracker{state_tracker_}, program{std::move(program_)}, assembly_programs{std::move(
@@ -74,6 +104,10 @@ GraphicsProgram::GraphicsProgram(TextureCache& texture_cache_, BufferCache& buff
74 } 104 }
75 ASSERT(num_textures <= MAX_TEXTURES); 105 ASSERT(num_textures <= MAX_TEXTURES);
76 ASSERT(num_images <= MAX_IMAGES); 106 ASSERT(num_images <= MAX_IMAGES);
107
108 if (assembly_programs[0].handle != 0 && xfb_state) {
109 GenerateTransformFeedbackState(*xfb_state);
110 }
77} 111}
78 112
79struct Spec { 113struct Spec {
@@ -302,4 +336,56 @@ void GraphicsProgram::Configure(bool is_indexed) {
302 } 336 }
303} 337}
304 338
339void GraphicsProgram::GenerateTransformFeedbackState(
340 const VideoCommon::TransformFeedbackState& xfb_state) {
341 // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
342 // when this is required.
343 const auto& regs{maxwell3d.regs};
344
345 GLint* cursor{xfb_attribs.data()};
346 GLint* current_stream{xfb_streams.data()};
347
348 for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
349 const auto& layout = regs.tfb_layouts[feedback];
350 UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding");
351 if (layout.varying_count == 0) {
352 continue;
353 }
354 *current_stream = static_cast<GLint>(feedback);
355 if (current_stream != xfb_streams.data()) {
356 // When stepping one stream, push the expected token
357 cursor[0] = GL_NEXT_BUFFER_NV;
358 cursor[1] = 0;
359 cursor[2] = 0;
360 cursor += XFB_ENTRY_STRIDE;
361 }
362 ++current_stream;
363
364 const auto& locations = regs.tfb_varying_locs[feedback];
365 std::optional<u8> current_index;
366 for (u32 offset = 0; offset < layout.varying_count; ++offset) {
367 const u8 location = locations[offset];
368 const u8 index = location / 4;
369
370 if (current_index == index) {
371 // Increase number of components of the previous attachment
372 ++cursor[-2];
373 continue;
374 }
375 current_index = index;
376
377 std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location);
378 cursor[1] = 1;
379 cursor += XFB_ENTRY_STRIDE;
380 }
381 }
382 num_xfb_attribs = static_cast<GLsizei>((cursor - xfb_attribs.data()) / XFB_ENTRY_STRIDE);
383 num_xfb_strides = static_cast<GLsizei>(current_stream - xfb_streams.data());
384}
385
386void GraphicsProgram::ConfigureTransformFeedbackImpl() const {
387 glTransformFeedbackStreamAttribsNV(num_xfb_attribs, xfb_attribs.data(), num_xfb_strides,
388 xfb_streams.data(), GL_INTERLEAVED_ATTRIBS);
389}
390
305} // namespace OpenGL 391} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_graphics_program.h b/src/video_core/renderer_opengl/gl_graphics_program.h
index 18292bb16..53a57ede5 100644
--- a/src/video_core/renderer_opengl/gl_graphics_program.h
+++ b/src/video_core/renderer_opengl/gl_graphics_program.h
@@ -16,6 +16,7 @@
16#include "video_core/renderer_opengl/gl_buffer_cache.h" 16#include "video_core/renderer_opengl/gl_buffer_cache.h"
17#include "video_core/renderer_opengl/gl_resource_manager.h" 17#include "video_core/renderer_opengl/gl_resource_manager.h"
18#include "video_core/renderer_opengl/gl_texture_cache.h" 18#include "video_core/renderer_opengl/gl_texture_cache.h"
19#include "video_core/transform_feedback.h"
19 20
20namespace OpenGL { 21namespace OpenGL {
21 22
@@ -24,16 +25,6 @@ class ProgramManager;
24using Maxwell = Tegra::Engines::Maxwell3D::Regs; 25using Maxwell = Tegra::Engines::Maxwell3D::Regs;
25 26
26struct GraphicsProgramKey { 27struct GraphicsProgramKey {
27 struct TransformFeedbackState {
28 struct Layout {
29 u32 stream;
30 u32 varying_count;
31 u32 stride;
32 };
33 std::array<Layout, Maxwell::NumTransformFeedbackBuffers> layouts;
34 std::array<std::array<u8, 128>, Maxwell::NumTransformFeedbackBuffers> varyings;
35 };
36
37 std::array<u64, 6> unique_hashes; 28 std::array<u64, 6> unique_hashes;
38 union { 29 union {
39 u32 raw; 30 u32 raw;
@@ -45,7 +36,7 @@ struct GraphicsProgramKey {
45 BitField<10, 1, u32> tessellation_clockwise; 36 BitField<10, 1, u32> tessellation_clockwise;
46 }; 37 };
47 std::array<u32, 3> padding; 38 std::array<u32, 3> padding;
48 TransformFeedbackState xfb_state; 39 VideoCommon::TransformFeedbackState xfb_state;
49 40
50 size_t Hash() const noexcept; 41 size_t Hash() const noexcept;
51 42
@@ -75,11 +66,22 @@ public:
75 ProgramManager& program_manager_, StateTracker& state_tracker_, 66 ProgramManager& program_manager_, StateTracker& state_tracker_,
76 OGLProgram program_, 67 OGLProgram program_,
77 std::array<OGLAssemblyProgram, 5> assembly_programs_, 68 std::array<OGLAssemblyProgram, 5> assembly_programs_,
78 const std::array<const Shader::Info*, 5>& infos); 69 const std::array<const Shader::Info*, 5>& infos,
70 const VideoCommon::TransformFeedbackState* xfb_state);
79 71
80 void Configure(bool is_indexed); 72 void Configure(bool is_indexed);
81 73
74 void ConfigureTransformFeedback() const {
75 if (num_xfb_attribs != 0) {
76 ConfigureTransformFeedbackImpl();
77 }
78 }
79
82private: 80private:
81 void GenerateTransformFeedbackState(const VideoCommon::TransformFeedbackState& xfb_state);
82
83 void ConfigureTransformFeedbackImpl() const;
84
83 TextureCache& texture_cache; 85 TextureCache& texture_cache;
84 BufferCache& buffer_cache; 86 BufferCache& buffer_cache;
85 Tegra::MemoryManager& gpu_memory; 87 Tegra::MemoryManager& gpu_memory;
@@ -96,6 +98,12 @@ private:
96 std::array<u32, 5> base_storage_bindings{}; 98 std::array<u32, 5> base_storage_bindings{};
97 std::array<u32, 5> num_texture_buffers{}; 99 std::array<u32, 5> num_texture_buffers{};
98 std::array<u32, 5> num_image_buffers{}; 100 std::array<u32, 5> num_image_buffers{};
101
102 static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
103 GLsizei num_xfb_attribs{};
104 GLsizei num_xfb_strides{};
105 std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
106 std::array<GLint, Maxwell::NumTransformFeedbackBuffers> xfb_streams{};
99}; 107};
100 108
101} // namespace OpenGL 109} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4834d58f0..51ff42ee9 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -51,37 +51,8 @@ MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192));
51MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Management", MP_RGB(100, 255, 100)); 51MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Management", MP_RGB(100, 255, 100));
52 52
53namespace { 53namespace {
54
55constexpr size_t NUM_SUPPORTED_VERTEX_ATTRIBUTES = 16; 54constexpr size_t NUM_SUPPORTED_VERTEX_ATTRIBUTES = 16;
56 55
57/// Translates hardware transform feedback indices
58/// @param location Hardware location
59/// @return Pair of ARB_transform_feedback3 token stream first and third arguments
60/// @note Read https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_transform_feedback3.txt
61std::pair<GLint, GLint> TransformFeedbackEnum(u8 location) {
62 const u8 index = location / 4;
63 if (index >= 8 && index <= 39) {
64 return {GL_GENERIC_ATTRIB_NV, index - 8};
65 }
66 if (index >= 48 && index <= 55) {
67 return {GL_TEXTURE_COORD_NV, index - 48};
68 }
69 switch (index) {
70 case 7:
71 return {GL_POSITION, 0};
72 case 40:
73 return {GL_PRIMARY_COLOR_NV, 0};
74 case 41:
75 return {GL_SECONDARY_COLOR_NV, 0};
76 case 42:
77 return {GL_BACK_PRIMARY_COLOR_NV, 0};
78 case 43:
79 return {GL_BACK_SECONDARY_COLOR_NV, 0};
80 }
81 UNIMPLEMENTED_MSG("index={}", index);
82 return {GL_POSITION, 0};
83}
84
85void oglEnable(GLenum cap, bool state) { 56void oglEnable(GLenum cap, bool state) {
86 (state ? glEnable : glDisable)(cap); 57 (state ? glEnable : glDisable)(cap);
87} 58}
@@ -253,7 +224,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
253 program->Configure(is_indexed); 224 program->Configure(is_indexed);
254 225
255 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d.regs.draw.topology); 226 const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d.regs.draw.topology);
256 BeginTransformFeedback(primitive_mode); 227 BeginTransformFeedback(program, primitive_mode);
257 228
258 const GLuint base_instance = static_cast<GLuint>(maxwell3d.regs.vb_base_instance); 229 const GLuint base_instance = static_cast<GLuint>(maxwell3d.regs.vb_base_instance);
259 const GLsizei num_instances = 230 const GLsizei num_instances =
@@ -1025,68 +996,13 @@ void RasterizerOpenGL::SyncFramebufferSRGB() {
1025 oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d.regs.framebuffer_srgb); 996 oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d.regs.framebuffer_srgb);
1026} 997}
1027 998
1028void RasterizerOpenGL::SyncTransformFeedback() { 999void RasterizerOpenGL::BeginTransformFeedback(GraphicsProgram* program, GLenum primitive_mode) {
1029 // TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
1030 // when this is required.
1031 const auto& regs = maxwell3d.regs;
1032
1033 static constexpr std::size_t STRIDE = 3;
1034 std::array<GLint, 128 * STRIDE * Maxwell::NumTransformFeedbackBuffers> attribs;
1035 std::array<GLint, Maxwell::NumTransformFeedbackBuffers> streams;
1036
1037 GLint* cursor = attribs.data();
1038 GLint* current_stream = streams.data();
1039
1040 for (std::size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
1041 const auto& layout = regs.tfb_layouts[feedback];
1042 UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding");
1043 if (layout.varying_count == 0) {
1044 continue;
1045 }
1046
1047 *current_stream = static_cast<GLint>(feedback);
1048 if (current_stream != streams.data()) {
1049 // When stepping one stream, push the expected token
1050 cursor[0] = GL_NEXT_BUFFER_NV;
1051 cursor[1] = 0;
1052 cursor[2] = 0;
1053 cursor += STRIDE;
1054 }
1055 ++current_stream;
1056
1057 const auto& locations = regs.tfb_varying_locs[feedback];
1058 std::optional<u8> current_index;
1059 for (u32 offset = 0; offset < layout.varying_count; ++offset) {
1060 const u8 location = locations[offset];
1061 const u8 index = location / 4;
1062
1063 if (current_index == index) {
1064 // Increase number of components of the previous attachment
1065 ++cursor[-2];
1066 continue;
1067 }
1068 current_index = index;
1069
1070 std::tie(cursor[0], cursor[2]) = TransformFeedbackEnum(location);
1071 cursor[1] = 1;
1072 cursor += STRIDE;
1073 }
1074 }
1075
1076 const GLsizei num_attribs = static_cast<GLsizei>((cursor - attribs.data()) / STRIDE);
1077 const GLsizei num_strides = static_cast<GLsizei>(current_stream - streams.data());
1078 glTransformFeedbackStreamAttribsNV(num_attribs, attribs.data(), num_strides, streams.data(),
1079 GL_INTERLEAVED_ATTRIBS);
1080}
1081
1082void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
1083 const auto& regs = maxwell3d.regs; 1000 const auto& regs = maxwell3d.regs;
1084 if (regs.tfb_enabled == 0) { 1001 if (regs.tfb_enabled == 0) {
1085 return; 1002 return;
1086 } 1003 }
1087 if (device.UseAssemblyShaders()) { 1004 program->ConfigureTransformFeedback();
1088 SyncTransformFeedback(); 1005
1089 }
1090 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || 1006 UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) ||
1091 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || 1007 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) ||
1092 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); 1008 regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry));
@@ -1100,11 +1016,9 @@ void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
1100} 1016}
1101 1017
1102void RasterizerOpenGL::EndTransformFeedback() { 1018void RasterizerOpenGL::EndTransformFeedback() {
1103 const auto& regs = maxwell3d.regs; 1019 if (maxwell3d.regs.tfb_enabled != 0) {
1104 if (regs.tfb_enabled == 0) { 1020 glEndTransformFeedback();
1105 return;
1106 } 1021 }
1107 glEndTransformFeedback();
1108} 1022}
1109 1023
1110AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {} 1024AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 2fdcbe4ba..08f509c19 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -193,12 +193,8 @@ private:
193 /// Syncs vertex instances to match the guest state 193 /// Syncs vertex instances to match the guest state
194 void SyncVertexInstances(); 194 void SyncVertexInstances();
195 195
196 /// Syncs transform feedback state to match guest state
197 /// @note Only valid on assembly shaders
198 void SyncTransformFeedback();
199
200 /// Begin a transform feedback 196 /// Begin a transform feedback
201 void BeginTransformFeedback(GLenum primitive_mode); 197 void BeginTransformFeedback(GraphicsProgram* program, GLenum primitive_mode);
202 198
203 /// End a transform feedback 199 /// End a transform feedback
204 void EndTransformFeedback(); 200 void EndTransformFeedback();
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index b4f26dd74..0a0f1324f 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -254,6 +254,17 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsProgramKey& key,
254 } 254 }
255 return info; 255 return info;
256} 256}
257
258void SetXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) {
259 std::ranges::transform(regs.tfb_layouts, state.layouts.begin(), [](const auto& layout) {
260 return VideoCommon::TransformFeedbackState::Layout{
261 .stream = layout.stream,
262 .varying_count = layout.varying_count,
263 .stride = layout.stride,
264 };
265 });
266 state.varyings = regs.tfb_varying_locs;
267}
257} // Anonymous namespace 268} // Anonymous namespace
258 269
259ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_, 270ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_,
@@ -282,7 +293,10 @@ GraphicsProgram* ShaderCache::CurrentGraphicsProgram() {
282 graphics_key.tessellation_primitive.Assign(regs.tess_mode.prim.Value()); 293 graphics_key.tessellation_primitive.Assign(regs.tess_mode.prim.Value());
283 graphics_key.tessellation_spacing.Assign(regs.tess_mode.spacing.Value()); 294 graphics_key.tessellation_spacing.Assign(regs.tess_mode.spacing.Value());
284 graphics_key.tessellation_clockwise.Assign(regs.tess_mode.cw.Value()); 295 graphics_key.tessellation_clockwise.Assign(regs.tess_mode.cw.Value());
285 296 graphics_key.xfb_enabled.Assign(regs.tfb_enabled != 0 ? 1 : 0);
297 if (graphics_key.xfb_enabled) {
298 SetXfbState(graphics_key.xfb_state, regs);
299 }
286 const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)}; 300 const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
287 auto& program{pair->second}; 301 auto& program{pair->second};
288 if (is_new) { 302 if (is_new) {
@@ -368,7 +382,8 @@ std::unique_ptr<GraphicsProgram> ShaderCache::CreateGraphicsProgram(
368 } 382 }
369 return std::make_unique<GraphicsProgram>( 383 return std::make_unique<GraphicsProgram>(
370 texture_cache, buffer_cache, gpu_memory, maxwell3d, program_manager, state_tracker, 384 texture_cache, buffer_cache, gpu_memory, maxwell3d, program_manager, state_tracker,
371 std::move(source_program), std::move(assembly_programs), infos); 385 std::move(source_program), std::move(assembly_programs), infos,
386 key.xfb_enabled != 0 ? &key.xfb_state : nullptr);
372} 387}
373 388
374std::unique_ptr<ComputeProgram> ShaderCache::CreateComputeProgram( 389std::unique_ptr<ComputeProgram> ShaderCache::CreateComputeProgram(