summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/shader_recompiler/CMakeLists.txt1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp63
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h22
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h8
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp56
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp22
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h3
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc8
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp16
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp85
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp4
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp135
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp22
-rw-r--r--src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp2
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp2
-rw-r--r--src/shader_recompiler/shader_info.h6
16 files changed, 405 insertions, 50 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index fa268d38f..755db5dfa 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -88,6 +88,7 @@ add_library(shader_recompiler STATIC
88 frontend/maxwell/translate/impl/integer_shift_right.cpp 88 frontend/maxwell/translate/impl/integer_shift_right.cpp
89 frontend/maxwell/translate/impl/integer_short_multiply_add.cpp 89 frontend/maxwell/translate/impl/integer_short_multiply_add.cpp
90 frontend/maxwell/translate/impl/integer_to_integer_conversion.cpp 90 frontend/maxwell/translate/impl/integer_to_integer_conversion.cpp
91 frontend/maxwell/translate/impl/load_constant.cpp
91 frontend/maxwell/translate/impl/load_effective_address.cpp 92 frontend/maxwell/translate/impl/load_effective_address.cpp
92 frontend/maxwell/translate/impl/load_store_attribute.cpp 93 frontend/maxwell/translate/impl/load_store_attribute.cpp
93 frontend/maxwell/translate/impl/load_store_memory.cpp 94 frontend/maxwell/translate/impl/load_store_memory.cpp
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 21900d387..278b26b50 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -104,15 +104,23 @@ void EmitContext::DefineCommonTypes(const Info& info) {
104 104
105 U1 = Name(TypeBool(), "u1"); 105 U1 = Name(TypeBool(), "u1");
106 106
107 // TODO: Conditionally define these
108 AddCapability(spv::Capability::Int16);
109 AddCapability(spv::Capability::Int64);
110 U16 = Name(TypeInt(16, false), "u16");
111 U64 = Name(TypeInt(64, false), "u64");
112
113 F32.Define(*this, TypeFloat(32), "f32"); 107 F32.Define(*this, TypeFloat(32), "f32");
114 U32.Define(*this, TypeInt(32, false), "u32"); 108 U32.Define(*this, TypeInt(32, false), "u32");
115 109
110 if (info.uses_int8) {
111 AddCapability(spv::Capability::Int8);
112 U8 = Name(TypeInt(8, false), "u8");
113 S8 = Name(TypeInt(8, true), "s8");
114 }
115 if (info.uses_int16) {
116 AddCapability(spv::Capability::Int16);
117 U16 = Name(TypeInt(16, false), "u16");
118 S16 = Name(TypeInt(16, true), "s16");
119 }
120 if (info.uses_int64) {
121 AddCapability(spv::Capability::Int64);
122 U64 = Name(TypeInt(64, false), "u64");
123 }
116 if (info.uses_fp16) { 124 if (info.uses_fp16) {
117 AddCapability(spv::Capability::Float16); 125 AddCapability(spv::Capability::Float16);
118 F16.Define(*this, TypeFloat(16), "f16"); 126 F16.Define(*this, TypeFloat(16), "f16");
@@ -151,26 +159,51 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
151 if (info.constant_buffer_descriptors.empty()) { 159 if (info.constant_buffer_descriptors.empty()) {
152 return; 160 return;
153 } 161 }
154 const Id array_type{TypeArray(U32[1], Constant(U32[1], 4096))}; 162 if (True(info.used_constant_buffer_types & IR::Type::U8)) {
155 Decorate(array_type, spv::Decoration::ArrayStride, 4U); 163 DefineConstantBuffers(info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8));
164 DefineConstantBuffers(info, &UniformDefinitions::S8, binding, S8, 's', sizeof(s8));
165 }
166 if (True(info.used_constant_buffer_types & IR::Type::U16)) {
167 DefineConstantBuffers(info, &UniformDefinitions::U16, binding, U16, 'u', sizeof(u16));
168 DefineConstantBuffers(info, &UniformDefinitions::S16, binding, S16, 's', sizeof(s16));
169 }
170 if (True(info.used_constant_buffer_types & IR::Type::U32)) {
171 DefineConstantBuffers(info, &UniformDefinitions::U32, binding, U32[1], 'u', sizeof(u32));
172 }
173 if (True(info.used_constant_buffer_types & IR::Type::F32)) {
174 DefineConstantBuffers(info, &UniformDefinitions::F32, binding, F32[1], 'f', sizeof(f32));
175 }
176 if (True(info.used_constant_buffer_types & IR::Type::U64)) {
177 DefineConstantBuffers(info, &UniformDefinitions::U64, binding, U64, 'u', sizeof(u64));
178 }
179 for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
180 binding += desc.count;
181 }
182}
183
184void EmitContext::DefineConstantBuffers(const Info& info, Id UniformDefinitions::*member_type,
185 u32 binding, Id type, char type_char, u32 element_size) {
186 const Id array_type{TypeArray(type, Constant(U32[1], 65536U / element_size))};
187 Decorate(array_type, spv::Decoration::ArrayStride, element_size);
156 188
157 const Id struct_type{TypeStruct(array_type)}; 189 const Id struct_type{TypeStruct(array_type)};
158 Name(struct_type, "cbuf_block"); 190 Name(struct_type, fmt::format("cbuf_block_{}{}", type_char, element_size * CHAR_BIT));
159 Decorate(struct_type, spv::Decoration::Block); 191 Decorate(struct_type, spv::Decoration::Block);
160 MemberName(struct_type, 0, "data"); 192 MemberName(struct_type, 0, "data");
161 MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U); 193 MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U);
162 194
163 const Id uniform_type{TypePointer(spv::StorageClass::Uniform, struct_type)}; 195 const Id struct_pointer_type{TypePointer(spv::StorageClass::Uniform, struct_type)};
164 uniform_u32 = TypePointer(spv::StorageClass::Uniform, U32[1]); 196 const Id uniform_type{TypePointer(spv::StorageClass::Uniform, type)};
197 uniform_types.*member_type = uniform_type;
165 198
166 u32 index{};
167 for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) { 199 for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
168 const Id id{AddGlobalVariable(uniform_type, spv::StorageClass::Uniform)}; 200 const Id id{AddGlobalVariable(struct_pointer_type, spv::StorageClass::Uniform)};
169 Decorate(id, spv::Decoration::Binding, binding); 201 Decorate(id, spv::Decoration::Binding, binding);
170 Decorate(id, spv::Decoration::DescriptorSet, 0U); 202 Decorate(id, spv::Decoration::DescriptorSet, 0U);
171 Name(id, fmt::format("c{}", desc.index)); 203 Name(id, fmt::format("c{}", desc.index));
172 std::fill_n(cbufs.data() + desc.index, desc.count, id); 204 for (size_t i = 0; i < desc.count; ++i) {
173 index += desc.count; 205 cbufs[desc.index + i].*member_type = id;
206 }
174 binding += desc.count; 207 binding += desc.count;
175 } 208 }
176} 209}
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index 8b3109eb8..35eca258a 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -10,8 +10,8 @@
10#include <sirit/sirit.h> 10#include <sirit/sirit.h>
11 11
12#include "shader_recompiler/frontend/ir/program.h" 12#include "shader_recompiler/frontend/ir/program.h"
13#include "shader_recompiler/shader_info.h"
14#include "shader_recompiler/profile.h" 13#include "shader_recompiler/profile.h"
14#include "shader_recompiler/shader_info.h"
15 15
16namespace Shader::Backend::SPIRV { 16namespace Shader::Backend::SPIRV {
17 17
@@ -34,6 +34,16 @@ struct TextureDefinition {
34 Id type; 34 Id type;
35}; 35};
36 36
37struct UniformDefinitions {
38 Id U8{};
39 Id S8{};
40 Id U16{};
41 Id S16{};
42 Id U32{};
43 Id F32{};
44 Id U64{};
45};
46
37class EmitContext final : public Sirit::Module { 47class EmitContext final : public Sirit::Module {
38public: 48public:
39 explicit EmitContext(const Profile& profile, IR::Program& program); 49 explicit EmitContext(const Profile& profile, IR::Program& program);
@@ -45,7 +55,10 @@ public:
45 55
46 Id void_id{}; 56 Id void_id{};
47 Id U1{}; 57 Id U1{};
58 Id U8{};
59 Id S8{};
48 Id U16{}; 60 Id U16{};
61 Id S16{};
49 Id U64{}; 62 Id U64{};
50 VectorTypes F32; 63 VectorTypes F32;
51 VectorTypes U32; 64 VectorTypes U32;
@@ -56,10 +69,11 @@ public:
56 Id false_value{}; 69 Id false_value{};
57 Id u32_zero_value{}; 70 Id u32_zero_value{};
58 71
59 Id uniform_u32{}; 72 UniformDefinitions uniform_types;
73
60 Id storage_u32{}; 74 Id storage_u32{};
61 75
62 std::array<Id, Info::MAX_CBUFS> cbufs{}; 76 std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
63 std::array<Id, Info::MAX_SSBOS> ssbos{}; 77 std::array<Id, Info::MAX_SSBOS> ssbos{};
64 std::vector<TextureDefinition> textures; 78 std::vector<TextureDefinition> textures;
65 79
@@ -71,6 +85,8 @@ private:
71 void DefineCommonConstants(); 85 void DefineCommonConstants();
72 void DefineSpecialVariables(const Info& info); 86 void DefineSpecialVariables(const Info& info);
73 void DefineConstantBuffers(const Info& info, u32& binding); 87 void DefineConstantBuffers(const Info& info, u32& binding);
88 void DefineConstantBuffers(const Info& info, Id UniformDefinitions::*member_type, u32 binding,
89 Id type, char type_char, u32 element_size);
74 void DefineStorageBuffers(const Info& info, u32& binding); 90 void DefineStorageBuffers(const Info& info, u32& binding);
75 void DefineTextures(const Info& info, u32& binding); 91 void DefineTextures(const Info& info, u32& binding);
76 void DefineLabels(IR::Program& program); 92 void DefineLabels(IR::Program& program);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 69698c478..aafc59bbb 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -34,7 +34,13 @@ void EmitGetPred(EmitContext& ctx);
34void EmitSetPred(EmitContext& ctx); 34void EmitSetPred(EmitContext& ctx);
35void EmitSetGotoVariable(EmitContext& ctx); 35void EmitSetGotoVariable(EmitContext& ctx);
36void EmitGetGotoVariable(EmitContext& ctx); 36void EmitGetGotoVariable(EmitContext& ctx);
37Id EmitGetCbuf(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); 37Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
38Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
39Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
40Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
41Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
42Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
43Id EmitGetCbufU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
38void EmitGetAttribute(EmitContext& ctx); 44void EmitGetAttribute(EmitContext& ctx);
39void EmitSetAttribute(EmitContext& ctx); 45void EmitSetAttribute(EmitContext& ctx);
40void EmitGetAttributeIndexed(EmitContext& ctx); 46void EmitGetAttributeIndexed(EmitContext& ctx);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index eb9c01c5a..125b58cf7 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -30,17 +30,61 @@ void EmitGetGotoVariable(EmitContext&) {
30 throw NotImplementedException("SPIR-V Instruction"); 30 throw NotImplementedException("SPIR-V Instruction");
31} 31}
32 32
33Id EmitGetCbuf(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 33static Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr,
34 u32 element_size, const IR::Value& binding, const IR::Value& offset) {
34 if (!binding.IsImmediate()) { 35 if (!binding.IsImmediate()) {
35 throw NotImplementedException("Constant buffer indexing"); 36 throw NotImplementedException("Constant buffer indexing");
36 } 37 }
38 const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr};
39 const Id uniform_type{ctx.uniform_types.*member_ptr};
37 if (!offset.IsImmediate()) { 40 if (!offset.IsImmediate()) {
38 throw NotImplementedException("Variable constant buffer offset"); 41 Id index{ctx.Def(offset)};
42 if (element_size > 1) {
43 const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))};
44 const Id shift{ctx.Constant(ctx.U32[1], log2_element_size)};
45 index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift);
46 }
47 const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)};
48 return ctx.OpLoad(result_type, access_chain);
39 } 49 }
40 const Id imm_offset{ctx.Constant(ctx.U32[1], offset.U32() / 4)}; 50 if (offset.U32() % element_size != 0) {
41 const Id cbuf{ctx.cbufs[binding.U32()]}; 51 throw NotImplementedException("Unaligned immediate constant buffer load");
42 const Id access_chain{ctx.OpAccessChain(ctx.uniform_u32, cbuf, ctx.u32_zero_value, imm_offset)}; 52 }
43 return ctx.OpLoad(ctx.U32[1], access_chain); 53 const Id imm_offset{ctx.Constant(ctx.U32[1], offset.U32() / element_size)};
54 const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)};
55 return ctx.OpLoad(result_type, access_chain);
56}
57
58Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
59 const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)};
60 return ctx.OpUConvert(ctx.U32[1], load);
61}
62
63Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
64 const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset)};
65 return ctx.OpSConvert(ctx.U32[1], load);
66}
67
68Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
69 const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset)};
70 return ctx.OpUConvert(ctx.U32[1], load);
71}
72
73Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
74 const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset)};
75 return ctx.OpSConvert(ctx.U32[1], load);
76}
77
78Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
79 return GetCbuf(ctx, ctx.U32[1], &UniformDefinitions::U32, sizeof(u32), binding, offset);
80}
81
82Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
83 return GetCbuf(ctx, ctx.F32[1], &UniformDefinitions::F32, sizeof(f32), binding, offset);
84}
85
86Id EmitGetCbufU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
87 return GetCbuf(ctx, ctx.U64, &UniformDefinitions::U64, sizeof(u64), binding, offset);
44} 88}
45 89
46void EmitGetAttribute(EmitContext&) { 90void EmitGetAttribute(EmitContext&) {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index ae3354c66..33819dd36 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -112,7 +112,27 @@ void IREmitter::SetPred(IR::Pred pred, const U1& value) {
112} 112}
113 113
114U32 IREmitter::GetCbuf(const U32& binding, const U32& byte_offset) { 114U32 IREmitter::GetCbuf(const U32& binding, const U32& byte_offset) {
115 return Inst<U32>(Opcode::GetCbuf, binding, byte_offset); 115 return Inst<U32>(Opcode::GetCbufU32, binding, byte_offset);
116}
117
118UAny IREmitter::GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize,
119 bool is_signed) {
120 switch (bitsize) {
121 case 8:
122 return Inst<U32>(is_signed ? Opcode::GetCbufS8 : Opcode::GetCbufU8, binding, byte_offset);
123 case 16:
124 return Inst<U32>(is_signed ? Opcode::GetCbufS16 : Opcode::GetCbufU16, binding, byte_offset);
125 case 32:
126 return Inst<U32>(Opcode::GetCbufU32, binding, byte_offset);
127 case 64:
128 return Inst<U64>(Opcode::GetCbufU64, binding, byte_offset);
129 default:
130 throw InvalidArgument("Invalid bit size {}", bitsize);
131 }
132}
133
134F32 IREmitter::GetFloatCbuf(const U32& binding, const U32& byte_offset) {
135 return Inst<F32>(Opcode::GetCbufF32, binding, byte_offset);
116} 136}
117 137
118U1 IREmitter::GetZFlag() { 138U1 IREmitter::GetZFlag() {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index cb2a7710a..e4d110540 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -47,6 +47,9 @@ public:
47 void SetGotoVariable(u32 id, const U1& value); 47 void SetGotoVariable(u32 id, const U1& value);
48 48
49 [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset); 49 [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset);
50 [[nodiscard]] UAny GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize,
51 bool is_signed);
52 [[nodiscard]] F32 GetFloatCbuf(const U32& binding, const U32& byte_offset);
50 53
51 [[nodiscard]] U1 GetZFlag(); 54 [[nodiscard]] U1 GetZFlag();
52 [[nodiscard]] U1 GetSFlag(); 55 [[nodiscard]] U1 GetSFlag();
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index aa011fab1..64bd495ed 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -21,7 +21,13 @@ OPCODE(GetPred, U1, Pred
21OPCODE(SetPred, Void, Pred, U1, ) 21OPCODE(SetPred, Void, Pred, U1, )
22OPCODE(GetGotoVariable, U1, U32, ) 22OPCODE(GetGotoVariable, U1, U32, )
23OPCODE(SetGotoVariable, Void, U32, U1, ) 23OPCODE(SetGotoVariable, Void, U32, U1, )
24OPCODE(GetCbuf, U32, U32, U32, ) 24OPCODE(GetCbufU8, U32, U32, U32, )
25OPCODE(GetCbufS8, U32, U32, U32, )
26OPCODE(GetCbufU16, U32, U32, U32, )
27OPCODE(GetCbufS16, U32, U32, U32, )
28OPCODE(GetCbufU32, U32, U32, U32, )
29OPCODE(GetCbufF32, F32, U32, U32, )
30OPCODE(GetCbufU64, U64, U32, U32, )
25OPCODE(GetAttribute, U32, Attribute, ) 31OPCODE(GetAttribute, U32, Attribute, )
26OPCODE(SetAttribute, Void, Attribute, U32, ) 32OPCODE(SetAttribute, Void, Attribute, U32, )
27OPCODE(GetAttributeIndexed, U32, U32, ) 33OPCODE(GetAttributeIndexed, U32, U32, )
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
index a5a0e1a9b..7564aeeb2 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
@@ -56,25 +56,32 @@ IR::F32 TranslatorVisitor::GetFloatReg39(u64 insn) {
56 return ir.BitCast<IR::F32>(GetReg39(insn)); 56 return ir.BitCast<IR::F32>(GetReg39(insn));
57} 57}
58 58
59IR::U32 TranslatorVisitor::GetCbuf(u64 insn) { 59static std::pair<IR::U32, IR::U32> CbufAddr(u64 insn) {
60 union { 60 union {
61 u64 raw; 61 u64 raw;
62 BitField<20, 14, s64> offset; 62 BitField<20, 14, s64> offset;
63 BitField<34, 5, u64> binding; 63 BitField<34, 5, u64> binding;
64 } const cbuf{insn}; 64 } const cbuf{insn};
65
65 if (cbuf.binding >= 18) { 66 if (cbuf.binding >= 18) {
66 throw NotImplementedException("Out of bounds constant buffer binding {}", cbuf.binding); 67 throw NotImplementedException("Out of bounds constant buffer binding {}", cbuf.binding);
67 } 68 }
68 if (cbuf.offset >= 0x10'000 || cbuf.offset < 0) { 69 if (cbuf.offset >= 0x10'000 || cbuf.offset < 0) {
69 throw NotImplementedException("Out of bounds constant buffer offset {}", cbuf.offset); 70 throw NotImplementedException("Out of bounds constant buffer offset {}", cbuf.offset);
70 } 71 }
71 const IR::U32 binding{ir.Imm32(static_cast<u32>(cbuf.binding))}; 72 const IR::Value binding{static_cast<u32>(cbuf.binding)};
72 const IR::U32 byte_offset{ir.Imm32(static_cast<u32>(cbuf.offset) * 4)}; 73 const IR::Value byte_offset{static_cast<u32>(cbuf.offset) * 4};
74 return {IR::U32{binding}, IR::U32{byte_offset}};
75}
76
77IR::U32 TranslatorVisitor::GetCbuf(u64 insn) {
78 const auto[binding, byte_offset]{CbufAddr(insn)};
73 return ir.GetCbuf(binding, byte_offset); 79 return ir.GetCbuf(binding, byte_offset);
74} 80}
75 81
76IR::F32 TranslatorVisitor::GetFloatCbuf(u64 insn) { 82IR::F32 TranslatorVisitor::GetFloatCbuf(u64 insn) {
77 return ir.BitCast<IR::F32>(GetCbuf(insn)); 83 const auto[binding, byte_offset]{CbufAddr(insn)};
84 return ir.GetFloatCbuf(binding, byte_offset);
78} 85}
79 86
80IR::U32 TranslatorVisitor::GetImm20(u64 insn) { 87IR::U32 TranslatorVisitor::GetImm20(u64 insn) {
@@ -83,6 +90,7 @@ IR::U32 TranslatorVisitor::GetImm20(u64 insn) {
83 BitField<20, 19, u64> value; 90 BitField<20, 19, u64> value;
84 BitField<56, 1, u64> is_negative; 91 BitField<56, 1, u64> is_negative;
85 } const imm{insn}; 92 } const imm{insn};
93
86 if (imm.is_negative != 0) { 94 if (imm.is_negative != 0) {
87 const s64 raw{static_cast<s64>(imm.value)}; 95 const s64 raw{static_cast<s64>(imm.value)};
88 return ir.Imm32(static_cast<s32>(-(1LL << 19) + raw)); 96 return ir.Imm32(static_cast<s32>(-(1LL << 19) + raw));
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp
new file mode 100644
index 000000000..39becf93c
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp
@@ -0,0 +1,85 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/bit_field.h"
6#include "common/common_types.h"
7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
8
9namespace Shader::Maxwell {
10namespace {
11enum class Mode : u64 {
12 Default,
13 IL,
14 IS,
15 ISL,
16};
17
18enum class Size : u64 {
19 U8,
20 S8,
21 U16,
22 S16,
23 B32,
24 B64,
25};
26
27std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index,
28 const IR::U32& reg, const IR::U32& imm) {
29 switch (mode) {
30 case Mode::Default:
31 return {imm_index, ir.IAdd(reg, imm)};
32 default:
33 break;
34 }
35 throw NotImplementedException("Mode {}", mode);
36}
37} // Anonymous namespace
38
39void TranslatorVisitor::LDC(u64 insn) {
40 union {
41 u64 raw;
42 BitField<0, 8, IR::Reg> dest_reg;
43 BitField<8, 8, IR::Reg> src_reg;
44 BitField<20, 16, s64> offset;
45 BitField<36, 5, u64> index;
46 BitField<44, 2, Mode> mode;
47 BitField<48, 3, Size> size;
48 } const ldc{insn};
49
50 const IR::U32 imm_index{ir.Imm32(static_cast<u32>(ldc.index))};
51 const IR::U32 reg{X(ldc.src_reg)};
52 const IR::U32 imm{ir.Imm32(static_cast<s32>(ldc.offset))};
53 const auto [index, offset]{Slot(ir, ldc.mode, imm_index, reg, imm)};
54 switch (ldc.size) {
55 case Size::U8:
56 X(ldc.dest_reg, ir.GetCbuf(index, offset, 8, false));
57 break;
58 case Size::S8:
59 X(ldc.dest_reg, ir.GetCbuf(index, offset, 8, true));
60 break;
61 case Size::U16:
62 X(ldc.dest_reg, ir.GetCbuf(index, offset, 16, false));
63 break;
64 case Size::S16:
65 X(ldc.dest_reg, ir.GetCbuf(index, offset, 16, true));
66 break;
67 case Size::B32:
68 X(ldc.dest_reg, ir.GetCbuf(index, offset, 32, false));
69 break;
70 case Size::B64: {
71 if (!IR::IsAligned(ldc.dest_reg, 2)) {
72 throw NotImplementedException("Unaligned destination register");
73 }
74 const IR::Value vector{ir.UnpackUint2x32(ir.GetCbuf(index, offset, 64, false))};
75 for (int i = 0; i < 2; ++i) {
76 X(ldc.dest_reg + i, IR::U32{ir.CompositeExtract(vector, i)});
77 }
78 break;
79 }
80 default:
81 throw NotImplementedException("Invalid size {}", ldc.size.Value());
82 }
83}
84
85} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
index ff429c126..5b153acff 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
@@ -425,10 +425,6 @@ void TranslatorVisitor::LD(u64) {
425 ThrowNotImplemented(Opcode::LD); 425 ThrowNotImplemented(Opcode::LD);
426} 426}
427 427
428void TranslatorVisitor::LDC(u64) {
429 ThrowNotImplemented(Opcode::LDC);
430}
431
432void TranslatorVisitor::LDL(u64) { 428void TranslatorVisitor::LDL(u64) {
433 ThrowNotImplemented(Opcode::LDL); 429 ThrowNotImplemented(Opcode::LDL);
434} 430}
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 960beadd4..cdbe85221 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -25,18 +25,13 @@ void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
25 25
26void VisitUsages(Info& info, IR::Inst& inst) { 26void VisitUsages(Info& info, IR::Inst& inst) {
27 switch (inst.Opcode()) { 27 switch (inst.Opcode()) {
28 case IR::Opcode::WorkgroupId:
29 info.uses_workgroup_id = true;
30 break;
31 case IR::Opcode::LocalInvocationId:
32 info.uses_local_invocation_id = true;
33 break;
34 case IR::Opcode::CompositeConstructF16x2: 28 case IR::Opcode::CompositeConstructF16x2:
35 case IR::Opcode::CompositeConstructF16x3: 29 case IR::Opcode::CompositeConstructF16x3:
36 case IR::Opcode::CompositeConstructF16x4: 30 case IR::Opcode::CompositeConstructF16x4:
37 case IR::Opcode::CompositeExtractF16x2: 31 case IR::Opcode::CompositeExtractF16x2:
38 case IR::Opcode::CompositeExtractF16x3: 32 case IR::Opcode::CompositeExtractF16x3:
39 case IR::Opcode::CompositeExtractF16x4: 33 case IR::Opcode::CompositeExtractF16x4:
34 case IR::Opcode::SelectF16:
40 case IR::Opcode::BitCastU16F16: 35 case IR::Opcode::BitCastU16F16:
41 case IR::Opcode::BitCastF16U16: 36 case IR::Opcode::BitCastF16U16:
42 case IR::Opcode::PackFloat2x16: 37 case IR::Opcode::PackFloat2x16:
@@ -75,13 +70,139 @@ void VisitUsages(Info& info, IR::Inst& inst) {
75 case IR::Opcode::FPTrunc64: 70 case IR::Opcode::FPTrunc64:
76 info.uses_fp64 = true; 71 info.uses_fp64 = true;
77 break; 72 break;
78 case IR::Opcode::GetCbuf: 73 default:
74 break;
75 }
76 switch (inst.Opcode()) {
77 case IR::Opcode::GetCbufU8:
78 case IR::Opcode::GetCbufS8:
79 case IR::Opcode::UndefU8:
80 case IR::Opcode::LoadGlobalU8:
81 case IR::Opcode::LoadGlobalS8:
82 case IR::Opcode::WriteGlobalU8:
83 case IR::Opcode::WriteGlobalS8:
84 case IR::Opcode::LoadStorageU8:
85 case IR::Opcode::LoadStorageS8:
86 case IR::Opcode::WriteStorageU8:
87 case IR::Opcode::WriteStorageS8:
88 case IR::Opcode::SelectU8:
89 info.uses_int8 = true;
90 break;
91 default:
92 break;
93 }
94 switch (inst.Opcode()) {
95 case IR::Opcode::GetCbufU16:
96 case IR::Opcode::GetCbufS16:
97 case IR::Opcode::UndefU16:
98 case IR::Opcode::LoadGlobalU16:
99 case IR::Opcode::LoadGlobalS16:
100 case IR::Opcode::WriteGlobalU16:
101 case IR::Opcode::WriteGlobalS16:
102 case IR::Opcode::LoadStorageU16:
103 case IR::Opcode::LoadStorageS16:
104 case IR::Opcode::WriteStorageU16:
105 case IR::Opcode::WriteStorageS16:
106 case IR::Opcode::SelectU16:
107 case IR::Opcode::BitCastU16F16:
108 case IR::Opcode::BitCastF16U16:
109 case IR::Opcode::ConvertS16F16:
110 case IR::Opcode::ConvertS16F32:
111 case IR::Opcode::ConvertS16F64:
112 case IR::Opcode::ConvertU16F16:
113 case IR::Opcode::ConvertU16F32:
114 case IR::Opcode::ConvertU16F64:
115 info.uses_int16 = true;
116 break;
117 default:
118 break;
119 }
120 switch (inst.Opcode()) {
121 case IR::Opcode::GetCbufU64:
122 case IR::Opcode::UndefU64:
123 case IR::Opcode::LoadGlobalU8:
124 case IR::Opcode::LoadGlobalS8:
125 case IR::Opcode::LoadGlobalU16:
126 case IR::Opcode::LoadGlobalS16:
127 case IR::Opcode::LoadGlobal32:
128 case IR::Opcode::LoadGlobal64:
129 case IR::Opcode::LoadGlobal128:
130 case IR::Opcode::WriteGlobalU8:
131 case IR::Opcode::WriteGlobalS8:
132 case IR::Opcode::WriteGlobalU16:
133 case IR::Opcode::WriteGlobalS16:
134 case IR::Opcode::WriteGlobal32:
135 case IR::Opcode::WriteGlobal64:
136 case IR::Opcode::WriteGlobal128:
137 case IR::Opcode::SelectU64:
138 case IR::Opcode::BitCastU64F64:
139 case IR::Opcode::BitCastF64U64:
140 case IR::Opcode::PackUint2x32:
141 case IR::Opcode::UnpackUint2x32:
142 case IR::Opcode::IAdd64:
143 case IR::Opcode::ISub64:
144 case IR::Opcode::INeg64:
145 case IR::Opcode::ShiftLeftLogical64:
146 case IR::Opcode::ShiftRightLogical64:
147 case IR::Opcode::ShiftRightArithmetic64:
148 case IR::Opcode::ConvertS64F16:
149 case IR::Opcode::ConvertS64F32:
150 case IR::Opcode::ConvertS64F64:
151 case IR::Opcode::ConvertU64F16:
152 case IR::Opcode::ConvertU64F32:
153 case IR::Opcode::ConvertU64F64:
154 case IR::Opcode::ConvertU64U32:
155 case IR::Opcode::ConvertU32U64:
156 case IR::Opcode::ConvertF16U64:
157 case IR::Opcode::ConvertF32U64:
158 case IR::Opcode::ConvertF64U64:
159 info.uses_int64 = true;
160 break;
161 default:
162 break;
163 }
164 switch (inst.Opcode()) {
165 case IR::Opcode::WorkgroupId:
166 info.uses_workgroup_id = true;
167 break;
168 case IR::Opcode::LocalInvocationId:
169 info.uses_local_invocation_id = true;
170 break;
171 case IR::Opcode::GetCbufU8:
172 case IR::Opcode::GetCbufS8:
173 case IR::Opcode::GetCbufU16:
174 case IR::Opcode::GetCbufS16:
175 case IR::Opcode::GetCbufU32:
176 case IR::Opcode::GetCbufF32:
177 case IR::Opcode::GetCbufU64: {
79 if (const IR::Value index{inst.Arg(0)}; index.IsImmediate()) { 178 if (const IR::Value index{inst.Arg(0)}; index.IsImmediate()) {
80 AddConstantBufferDescriptor(info, index.U32(), 1); 179 AddConstantBufferDescriptor(info, index.U32(), 1);
81 } else { 180 } else {
82 throw NotImplementedException("Constant buffer with non-immediate index"); 181 throw NotImplementedException("Constant buffer with non-immediate index");
83 } 182 }
183 switch (inst.Opcode()) {
184 case IR::Opcode::GetCbufU8:
185 case IR::Opcode::GetCbufS8:
186 info.used_constant_buffer_types |= IR::Type::U8;
187 break;
188 case IR::Opcode::GetCbufU16:
189 case IR::Opcode::GetCbufS16:
190 info.used_constant_buffer_types |= IR::Type::U16;
191 break;
192 case IR::Opcode::GetCbufU32:
193 info.used_constant_buffer_types |= IR::Type::U32;
194 break;
195 case IR::Opcode::GetCbufF32:
196 info.used_constant_buffer_types |= IR::Type::F32;
197 break;
198 case IR::Opcode::GetCbufU64:
199 info.used_constant_buffer_types |= IR::Type::U64;
200 break;
201 default:
202 break;
203 }
84 break; 204 break;
205 }
85 case IR::Opcode::BindlessImageSampleImplicitLod: 206 case IR::Opcode::BindlessImageSampleImplicitLod:
86 case IR::Opcode::BindlessImageSampleExplicitLod: 207 case IR::Opcode::BindlessImageSampleExplicitLod:
87 case IR::Opcode::BindlessImageSampleDrefImplicitLod: 208 case IR::Opcode::BindlessImageSampleDrefImplicitLod:
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index ae3d5a7d6..7ba9ebe9b 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -193,7 +193,7 @@ void FoldISub32(IR::Inst& inst) {
193 // ISub32 is generally used to subtract two constant buffers, compare and replace this with 193 // ISub32 is generally used to subtract two constant buffers, compare and replace this with
194 // zero if they equal. 194 // zero if they equal.
195 const auto equal_cbuf{[](IR::Inst* a, IR::Inst* b) { 195 const auto equal_cbuf{[](IR::Inst* a, IR::Inst* b) {
196 return a->Opcode() == IR::Opcode::GetCbuf && b->Opcode() == IR::Opcode::GetCbuf && 196 return a->Opcode() == IR::Opcode::GetCbufU32 && b->Opcode() == IR::Opcode::GetCbufU32 &&
197 a->Arg(0) == b->Arg(0) && a->Arg(1) == b->Arg(1); 197 a->Arg(0) == b->Arg(0) && a->Arg(1) == b->Arg(1);
198 }}; 198 }};
199 IR::Inst* op_a{inst.Arg(0).InstRecursive()}; 199 IR::Inst* op_a{inst.Arg(0).InstRecursive()};
@@ -207,7 +207,7 @@ void FoldISub32(IR::Inst& inst) {
207 // Canonicalize local variables to simplify the following logic 207 // Canonicalize local variables to simplify the following logic
208 std::swap(op_a, op_b); 208 std::swap(op_a, op_b);
209 } 209 }
210 if (op_b->Opcode() != IR::Opcode::GetCbuf) { 210 if (op_b->Opcode() != IR::Opcode::GetCbufU32) {
211 return; 211 return;
212 } 212 }
213 IR::Inst* const inst_cbuf{op_b}; 213 IR::Inst* const inst_cbuf{op_b};
@@ -277,7 +277,7 @@ void FoldLogicalNot(IR::Inst& inst) {
277 } 277 }
278} 278}
279 279
280template <typename Dest, typename Source> 280template <IR::Opcode op, typename Dest, typename Source>
281void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) { 281void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
282 const IR::Value value{inst.Arg(0)}; 282 const IR::Value value{inst.Arg(0)};
283 if (value.IsImmediate()) { 283 if (value.IsImmediate()) {
@@ -285,8 +285,18 @@ void FoldBitCast(IR::Inst& inst, IR::Opcode reverse) {
285 return; 285 return;
286 } 286 }
287 IR::Inst* const arg_inst{value.InstRecursive()}; 287 IR::Inst* const arg_inst{value.InstRecursive()};
288 if (value.InstRecursive()->Opcode() == reverse) { 288 if (arg_inst->Opcode() == reverse) {
289 inst.ReplaceUsesWith(arg_inst->Arg(0)); 289 inst.ReplaceUsesWith(arg_inst->Arg(0));
290 return;
291 }
292 if constexpr (op == IR::Opcode::BitCastF32U32) {
293 if (arg_inst->Opcode() == IR::Opcode::GetCbufU32) {
294 // Replace the bitcast with a typed constant buffer read
295 inst.ReplaceOpcode(IR::Opcode::GetCbufF32);
296 inst.SetArg(0, arg_inst->Arg(0));
297 inst.SetArg(1, arg_inst->Arg(1));
298 return;
299 }
290 } 300 }
291} 301}
292 302
@@ -325,9 +335,9 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
325 case IR::Opcode::ISub32: 335 case IR::Opcode::ISub32:
326 return FoldISub32(inst); 336 return FoldISub32(inst);
327 case IR::Opcode::BitCastF32U32: 337 case IR::Opcode::BitCastF32U32:
328 return FoldBitCast<f32, u32>(inst, IR::Opcode::BitCastU32F32); 338 return FoldBitCast<IR::Opcode::BitCastF32U32, f32, u32>(inst, IR::Opcode::BitCastU32F32);
329 case IR::Opcode::BitCastU32F32: 339 case IR::Opcode::BitCastU32F32:
330 return FoldBitCast<u32, f32>(inst, IR::Opcode::BitCastF32U32); 340 return FoldBitCast<IR::Opcode::BitCastU32F32, u32, f32>(inst, IR::Opcode::BitCastF32U32);
331 case IR::Opcode::IAdd64: 341 case IR::Opcode::IAdd64:
332 return FoldAdd<u64>(block, inst); 342 return FoldAdd<u64>(block, inst);
333 case IR::Opcode::SelectU32: 343 case IR::Opcode::SelectU32:
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
index 2625c0bb2..5d98d278e 100644
--- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
+++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp
@@ -203,7 +203,7 @@ std::optional<StorageBufferAddr> Track(IR::Block* block, const IR::Value& value,
203 return std::nullopt; 203 return std::nullopt;
204 } 204 }
205 const IR::Inst* const inst{value.InstRecursive()}; 205 const IR::Inst* const inst{value.InstRecursive()};
206 if (inst->Opcode() == IR::Opcode::GetCbuf) { 206 if (inst->Opcode() == IR::Opcode::GetCbufU32) {
207 const IR::Value index{inst->Arg(0)}; 207 const IR::Value index{inst->Arg(0)};
208 const IR::Value offset{inst->Arg(1)}; 208 const IR::Value offset{inst->Arg(1)};
209 if (!index.IsImmediate()) { 209 if (!index.IsImmediate()) {
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index 80e4ad6a9..ec802e02c 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -78,7 +78,7 @@ std::optional<ConstBufferAddr> Track(IR::Block* block, const IR::Value& value,
78 return std::nullopt; 78 return std::nullopt;
79 } 79 }
80 const IR::Inst* const inst{value.InstRecursive()}; 80 const IR::Inst* const inst{value.InstRecursive()};
81 if (inst->Opcode() == IR::Opcode::GetCbuf) { 81 if (inst->Opcode() == IR::Opcode::GetCbufU32) {
82 const IR::Value index{inst->Arg(0)}; 82 const IR::Value index{inst->Arg(0)};
83 const IR::Value offset{inst->Arg(1)}; 83 const IR::Value offset{inst->Arg(1)};
84 if (!index.IsImmediate()) { 84 if (!index.IsImmediate()) {
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index 103a2f0b4..adc1d9a64 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "shader_recompiler/frontend/ir/type.h"
10 11
11#include <boost/container/small_vector.hpp> 12#include <boost/container/small_vector.hpp>
12#include <boost/container/static_vector.hpp> 13#include <boost/container/static_vector.hpp>
@@ -61,10 +62,15 @@ struct Info {
61 bool uses_fp16_denorms_preserve{}; 62 bool uses_fp16_denorms_preserve{};
62 bool uses_fp32_denorms_flush{}; 63 bool uses_fp32_denorms_flush{};
63 bool uses_fp32_denorms_preserve{}; 64 bool uses_fp32_denorms_preserve{};
65 bool uses_int8{};
66 bool uses_int16{};
67 bool uses_int64{};
64 bool uses_image_1d{}; 68 bool uses_image_1d{};
65 bool uses_sampled_1d{}; 69 bool uses_sampled_1d{};
66 bool uses_sparse_residency{}; 70 bool uses_sparse_residency{};
67 71
72 IR::Type used_constant_buffer_types{};
73
68 u32 constant_buffer_mask{}; 74 u32 constant_buffer_mask{};
69 75
70 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS> 76 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>