Blender V4.3
shader_operation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <memory>
6#include <string>
7
8#include "BLI_assert.h"
9#include "BLI_listbase.h"
10#include "BLI_map.hh"
11#include "BLI_string_ref.hh"
12#include "BLI_utildefines.h"
13
15
16#include "GPU_context.hh"
17#include "GPU_material.hh"
18#include "GPU_shader.hh"
19#include "GPU_texture.hh"
20#include "GPU_uniform_buffer.hh"
21
23
26
27#include "COM_context.hh"
28#include "COM_operation.hh"
30#include "COM_result.hh"
31#include "COM_scheduler.hh"
32#include "COM_shader_node.hh"
34#include "COM_utilities.hh"
35
36#include <sstream>
37
39
40using namespace nodes::derived_node_tree_types;
41
43 PixelCompileUnit &compile_unit,
44 const Schedule &schedule)
45 : PixelOperation(context, compile_unit, schedule)
46{
48 GPU_MAT_COMPOSITOR, &construct_material, &generate_code, this);
50 GPU_material_compile(material_);
51}
52
57
59{
60 const Domain domain = compute_domain();
61 for (StringRef identifier : output_sockets_to_output_identifiers_map_.values()) {
62 Result &result = get_result(identifier);
63 result.allocate_texture(domain);
64 }
65
66 GPUShader *shader = GPU_material_get_shader(material_);
67 GPU_shader_bind(shader);
68
69 bind_material_resources(shader);
70 bind_inputs(shader);
71 bind_outputs(shader);
72
74
79}
80
81void ShaderOperation::bind_material_resources(GPUShader *shader)
82{
83 /* Bind the uniform buffer of the material if it exists. It may not exist if the GPU material has
84 * no uniforms. */
85 GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material_);
86 if (ubo) {
88 }
89
90 /* Bind color band textures needed by curve and ramp nodes. */
91 ListBase textures = GPU_material_textures(material_);
93 if (texture->colorband) {
94 const int texture_image_unit = GPU_shader_get_sampler_binding(shader, texture->sampler_name);
95 GPU_texture_bind(*texture->colorband, texture_image_unit);
96 }
97 }
98}
99
100void ShaderOperation::bind_inputs(GPUShader *shader)
101{
102 /* Attributes represents the inputs of the operation and their names match those of the inputs of
103 * the operation as well as the corresponding texture samples in the shader. */
104 ListBase attributes = GPU_material_attributes(material_);
105 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
106 get_input(attribute->name).bind_as_texture(shader, attribute->name);
107 }
108}
109
110void ShaderOperation::bind_outputs(GPUShader *shader)
111{
112 for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) {
113 get_result(output_identifier).bind_as_image(shader, output_identifier.c_str());
114 }
115}
116
117void ShaderOperation::construct_material(void *thunk, GPUMaterial *material)
118{
119 ShaderOperation *operation = static_cast<ShaderOperation *>(thunk);
120 operation->material_ = material;
121 for (DNode node : operation->compile_unit_) {
122 ShaderNode *shader_node = node->typeinfo->get_compositor_shader_node(node);
123 operation->shader_nodes_.add_new(node, std::unique_ptr<ShaderNode>(shader_node));
124
125 operation->link_node_inputs(node);
126
127 shader_node->compile(material);
128
129 operation->populate_results_for_node(node);
130 }
131}
132
133void ShaderOperation::link_node_inputs(DNode node)
134{
135 for (const bNodeSocket *input : node->input_sockets()) {
136 const DInputSocket dinput{node.context(), input};
137
138 /* Get the output linked to the input. If it is null, that means the input is unlinked.
139 * Unlinked inputs are linked by the node compile method, so skip this here. */
140 const DOutputSocket doutput = get_output_linked_to_input(dinput);
141 if (!doutput) {
142 continue;
143 }
144
145 /* If the origin node is part of the shader operation, then the link is internal to the GPU
146 * material graph and is linked appropriately. */
147 if (compile_unit_.contains(doutput.node())) {
148 link_node_input_internal(dinput, doutput);
149 continue;
150 }
151
152 /* Otherwise, the origin node is not part of the shader operation, then the link is external to
153 * the GPU material graph and an input to the shader operation must be declared and linked to
154 * the node input. */
155 link_node_input_external(dinput, doutput);
156 }
157}
158
159void ShaderOperation::link_node_input_internal(DInputSocket input_socket,
160 DOutputSocket output_socket)
161{
162 ShaderNode &output_node = *shader_nodes_.lookup(output_socket.node());
163 GPUNodeStack &output_stack = output_node.get_output(output_socket->identifier);
164
165 ShaderNode &input_node = *shader_nodes_.lookup(input_socket.node());
166 GPUNodeStack &input_stack = input_node.get_input(input_socket->identifier);
167
168 input_stack.link = output_stack.link;
169}
170
171void ShaderOperation::link_node_input_external(DInputSocket input_socket,
172 DOutputSocket output_socket)
173{
174
175 ShaderNode &node = *shader_nodes_.lookup(input_socket.node());
176 GPUNodeStack &stack = node.get_input(input_socket->identifier);
177
178 if (!output_to_material_attribute_map_.contains(output_socket)) {
179 /* No input was declared for that output yet, so declare it. */
180 declare_operation_input(input_socket, output_socket);
181 }
182 else {
183 /* An input was already declared for that same output socket, so no need to declare it again.
184 * But we update the domain priority of the input descriptor to be the higher priority of the
185 * existing descriptor and the descriptor of the new input socket. That's because the same
186 * output might be connected to multiple inputs inside the shader operation which have
187 * different proprieties. */
188 const std::string input_identifier = outputs_to_declared_inputs_map_.lookup(output_socket);
189 InputDescriptor &input_descriptor = this->get_input_descriptor(input_identifier);
190 input_descriptor.domain_priority = math::min(
191 input_descriptor.domain_priority,
193 }
194
195 /* Link the attribute representing the shader operation input corresponding to the given output
196 * socket. */
197 stack.link = output_to_material_attribute_map_.lookup(output_socket);
198}
199
200static const char *get_set_function_name(ResultType type)
201{
202 switch (type) {
204 return "set_value";
206 return "set_rgb";
208 return "set_rgba";
209 default:
210 /* Other types are internal and needn't be handled by operations. */
211 break;
212 }
213
215 return nullptr;
216}
217
218void ShaderOperation::declare_operation_input(DInputSocket input_socket,
219 DOutputSocket output_socket)
220{
221 const int input_index = output_to_material_attribute_map_.size();
222 std::string input_identifier = "input" + std::to_string(input_index);
223
224 /* Declare the input descriptor for this input and prefer to declare its type to be the same as
225 * the type of the output socket because doing type conversion in the shader is much cheaper. */
226 InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_socket.bsocket());
227 input_descriptor.type = get_node_socket_result_type(output_socket.bsocket());
228 declare_input_descriptor(input_identifier, input_descriptor);
229
230 /* Add a new GPU attribute representing an input to the GPU material. Instead of using the
231 * attribute directly, we link it to an appropriate set function and use its output link instead.
232 * This is needed because the `gputype` member of the attribute is only initialized if it is
233 * linked to a GPU node. */
234 GPUNodeLink *attribute_link;
235 GPU_link(material_,
236 get_set_function_name(input_descriptor.type),
237 GPU_attribute(material_, CD_AUTO_FROM_NAME, input_identifier.c_str()),
238 &attribute_link);
239
240 /* Map the output socket to the attribute that was created for it. */
241 output_to_material_attribute_map_.add(output_socket, attribute_link);
242
243 /* Map the identifier of the operation input to the output socket it is linked to. */
244 inputs_to_linked_outputs_map_.add_new(input_identifier, output_socket);
245
246 /* Map the output socket to the identifier of the operation input that was declared for it. */
247 outputs_to_declared_inputs_map_.add_new(output_socket, input_identifier);
248}
249
250void ShaderOperation::populate_results_for_node(DNode node)
251{
252 const DOutputSocket preview_output = find_preview_output_socket(node);
253
254 for (const bNodeSocket *output : node->output_sockets()) {
255 const DOutputSocket doutput{node.context(), output};
256
257 /* If any of the nodes linked to the output are not part of the shader operation but are part
258 * of the execution schedule, then an output result needs to be populated for it. */
259 const bool is_operation_output = is_output_linked_to_node_conditioned(
260 doutput,
261 [&](DNode node) { return schedule_.contains(node) && !compile_unit_.contains(node); });
262
263 /* If the output is used as the node preview, then an output result needs to be populated for
264 * it, and we additionally keep track of that output to later compute the previews from. */
265 const bool is_preview_output = doutput == preview_output;
266 if (is_preview_output) {
267 preview_outputs_.add(doutput);
268 }
269
270 if (is_operation_output || is_preview_output) {
271 populate_operation_result(doutput);
272 }
273 }
274}
275
276static const char *get_store_function_name(ResultType type)
277{
278 switch (type) {
280 return "node_compositor_store_output_float";
282 return "node_compositor_store_output_vector";
284 return "node_compositor_store_output_color";
285 default:
286 /* Other types are internal and needn't be handled by operations. */
287 break;
288 }
289
291 return nullptr;
292}
293
294void ShaderOperation::populate_operation_result(DOutputSocket output_socket)
295{
297 std::string output_identifier = "output" + std::to_string(output_id);
298
299 const ResultType result_type = get_node_socket_result_type(output_socket.bsocket());
300 const Result result = context().create_result(result_type);
301 populate_result(output_identifier, result);
302
303 /* Map the output socket to the identifier of the newly populated result. */
304 output_sockets_to_output_identifiers_map_.add_new(output_socket, output_identifier);
305
306 ShaderNode &node = *shader_nodes_.lookup(output_socket.node());
307 GPUNodeLink *output_link = node.get_output(output_socket->identifier).link;
308
309 /* Link the output node stack to an output storer storing in the appropriate result. The result
310 * is identified by its index in the operation and the index is encoded as a float to be passed
311 * to the GPU function. Additionally, create an output link from the storer node to declare as an
312 * output to the GPU material. This storer output link is a dummy link in the sense that its
313 * value is ignored since it is already written in the output, but it is used to track nodes that
314 * contribute to the output of the compositor node tree. */
315 GPUNodeLink *storer_output_link;
316 GPUNodeLink *id_link = GPU_constant((float *)&output_id);
317 const char *store_function_name = get_store_function_name(result_type);
318 GPU_link(material_, store_function_name, id_link, output_link, &storer_output_link);
319
320 /* Declare the output link of the storer node as an output of the GPU material to help the GPU
321 * code generator to track the nodes that contribute to the output of the shader. */
322 GPU_material_add_output_link_composite(material_, storer_output_link);
323}
324
325using namespace gpu::shader;
326
327void ShaderOperation::generate_code(void *thunk,
328 GPUMaterial *material,
329 GPUCodegenOutput *code_generator_output)
330{
331 ShaderOperation *operation = static_cast<ShaderOperation *>(thunk);
332 ShaderCreateInfo &shader_create_info = *reinterpret_cast<ShaderCreateInfo *>(
333 code_generator_output->create_info);
334
335 shader_create_info.local_group_size(16, 16);
336
337 /* The resources are added without explicit locations, so make sure it is done by the shader
338 * creator. */
339 shader_create_info.auto_resource_location(true);
340
341 /* Add implementation for implicit conversion operations inserted by the code generator. This
342 * file should include the functions [float|vec3|vec4]_from_[float|vec3|vec4]. */
343 shader_create_info.typedef_source("gpu_shader_compositor_type_conversion.glsl");
344
345 /* The source shader is a compute shader with a main function that calls the dynamically
346 * generated evaluate function. The evaluate function includes the serialized GPU material graph
347 * preceded by code that initialized the inputs of the operation. Additionally, the storer
348 * functions that writes the outputs are defined outside the evaluate function. */
349 shader_create_info.compute_source("gpu_shader_compositor_main.glsl");
350
351 /* The main function is emitted in the shader before the evaluate function, so the evaluate
352 * function needs to be forward declared here.
353 * NOTE(Metal): Metal does not require forward declarations. */
355 shader_create_info.typedef_source_generated += "void evaluate();\n";
356 }
357
358 operation->generate_code_for_outputs(shader_create_info);
359
360 shader_create_info.compute_source_generated += "void evaluate()\n{\n";
361
362 operation->generate_code_for_inputs(material, shader_create_info);
363
364 shader_create_info.compute_source_generated += code_generator_output->composite;
365
366 shader_create_info.compute_source_generated += "}\n";
367}
368
369/* Texture storers in the shader always take a vec4 as an argument, so encode each type in a vec4
370 * appropriately. */
372{
373 switch (type) {
375 return "vec4(value)";
377 return "vec4(vector, 0.0)";
379 return "color";
380 default:
381 /* Other types are internal and needn't be handled by operations. */
382 break;
383 }
384
386 return nullptr;
387}
388
389void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info)
390{
391 const std::string store_float_function_header = "void store_float(const uint id, float value)";
392 const std::string store_vector_function_header = "void store_vector(const uint id, vec3 vector)";
393 const std::string store_color_function_header = "void store_color(const uint id, vec4 color)";
394
395 /* The store functions are used by the node_compositor_store_output_[float|vector|color]
396 * functions but are only defined later as part of the compute source, so they need to be forward
397 * declared.
398 * NOTE(Metal): Metal does not require forward declarations. */
400 shader_create_info.typedef_source_generated += store_float_function_header + ";\n";
401 shader_create_info.typedef_source_generated += store_vector_function_header + ";\n";
402 shader_create_info.typedef_source_generated += store_color_function_header + ";\n";
403 }
404
405 /* Each of the store functions is essentially a single switch case on the given ID, so start by
406 * opening the function with a curly bracket followed by opening a switch statement in each of
407 * the functions. */
408 std::stringstream store_float_function;
409 std::stringstream store_vector_function;
410 std::stringstream store_color_function;
411 const std::string store_function_start = "\n{\n switch (id) {\n";
412 store_float_function << store_float_function_header << store_function_start;
413 store_vector_function << store_vector_function_header << store_function_start;
414 store_color_function << store_color_function_header << store_function_start;
415
417 const Result &result = get_result(output_identifier);
418
419 /* Add a write-only image for this output where its values will be written. */
420 shader_create_info.image(0,
421 result.get_gpu_texture_format(),
422 Qualifier::WRITE,
423 ImageType::FLOAT_2D,
424 output_identifier,
425 Frequency::PASS);
426
427 /* Add a case for the index of this output followed by a break statement. */
428 std::stringstream case_code;
429 const std::string store_expression = glsl_store_expression_from_result_type(result.type());
430 const std::string texel = ", ivec2(gl_GlobalInvocationID.xy), ";
431 case_code << " case " << StringRef(output_identifier).drop_known_prefix("output") << ":\n"
432 << " imageStore(" << output_identifier << texel << store_expression << ");\n"
433 << " break;\n";
434
435 /* Only add the case to the function with the matching type. */
436 switch (result.type()) {
438 store_float_function << case_code.str();
439 break;
441 store_vector_function << case_code.str();
442 break;
444 store_color_function << case_code.str();
445 break;
446 default:
447 /* Other types are internal and needn't be handled by operations. */
449 break;
450 }
451 }
452
453 /* Close the previously opened switch statement as well as the function itself. */
454 const std::string store_function_end = " }\n}\n\n";
455 store_float_function << store_function_end;
456 store_vector_function << store_function_end;
457 store_color_function << store_function_end;
458
459 shader_create_info.compute_source_generated += store_float_function.str() +
460 store_vector_function.str() +
461 store_color_function.str();
462}
463
465{
466 switch (type) {
468 return "float";
470 return "vec3";
472 return "vec4";
473 default:
474 /* Other types are internal and needn't be handled by operations. */
475 break;
476 }
477
479 return nullptr;
480}
481
482/* Texture loaders in the shader always return a vec4, so a swizzle is needed to retrieve the
483 * actual value for each type. */
485{
486 switch (type) {
488 return "x";
490 return "xyz";
492 return "rgba";
493 default:
494 /* Other types are internal and needn't be handled by operations. */
495 break;
496 }
497
499 return nullptr;
500}
501
502void ShaderOperation::generate_code_for_inputs(GPUMaterial *material,
503 ShaderCreateInfo &shader_create_info)
504{
505 /* The attributes of the GPU material represents the inputs of the operation. */
506 ListBase attributes = GPU_material_attributes(material);
507
508 if (BLI_listbase_is_empty(&attributes)) {
509 return;
510 }
511
512 /* Add a texture sampler for each of the inputs with the same name as the attribute. */
514 shader_create_info.sampler(0, ImageType::FLOAT_2D, attribute->name, Frequency::PASS);
515 }
516
517 /* Declare a struct called var_attrs that includes an appropriately typed member for each of the
518 * inputs. The names of the members should be the letter v followed by the ID of the attribute
519 * corresponding to the input. Such names are expected by the code generator. */
520 std::stringstream declare_attributes;
521 declare_attributes << "struct {\n";
522 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
523 const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
524 const std::string type = glsl_type_from_result_type(input_descriptor.type);
525 declare_attributes << " " << type << " v" << attribute->id << ";\n";
526 }
527 declare_attributes << "} var_attrs;\n\n";
528
529 shader_create_info.compute_source_generated += declare_attributes.str();
530
531 /* The texture loader utilities are needed to sample the input textures and initialize the
532 * attributes. */
533 shader_create_info.typedef_source("gpu_shader_compositor_texture_utilities.glsl");
534
535 /* Initialize each member of the previously declared struct by loading its corresponding texture
536 * with an appropriate swizzle for its type. */
537 std::stringstream initialize_attributes;
538 LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
539 const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
540 const std::string swizzle = glsl_swizzle_from_result_type(input_descriptor.type);
541 initialize_attributes << "var_attrs.v" << attribute->id << " = "
542 << "texture_load(" << attribute->name
543 << ", ivec2(gl_GlobalInvocationID.xy))." << swizzle << ";\n";
544 }
545 initialize_attributes << "\n";
546
547 shader_create_info.compute_source_generated += initialize_attributes.str();
548}
549
550} // namespace blender::realtime_compositor
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
unsigned int uint
@ CD_AUTO_FROM_NAME
struct ListBase ListBase
struct bNodeSocket bNodeSocket
eGPUBackendType GPU_backend_get_type()
GPUNodeLink * GPU_constant(const float *num)
void GPU_material_compile(GPUMaterial *mat)
ListBase GPU_material_attributes(const GPUMaterial *material)
void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status)
void GPU_material_free_single(GPUMaterial *material)
@ GPU_MAT_COMPOSITOR
GPUShader * GPU_material_get_shader(GPUMaterial *material)
ListBase GPU_material_textures(GPUMaterial *material)
void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link)
GPUMaterial * GPU_material_from_callbacks(eGPUMaterialEngine engine, ConstructGPUMaterialFn construct_function_cb, GPUCodegenCallbackFn generate_code_function_cb, void *thunk)
@ GPU_MAT_QUEUED
GPUNodeLink * GPU_attribute(GPUMaterial *mat, eCustomDataType type, const char *name)
bool GPU_link(GPUMaterial *mat, const char *name,...)
GPUUniformBuf * GPU_material_uniform_buffer_get(GPUMaterial *material)
int GPU_shader_get_sampler_binding(GPUShader *shader, const char *name)
int GPU_shader_get_ubo_binding(GPUShader *shader, const char *name)
void GPU_shader_bind(GPUShader *shader)
void GPU_shader_unbind()
void GPU_texture_bind(GPUTexture *texture, int unit)
void GPU_texture_image_unbind_all()
void GPU_texture_unbind_all()
#define GPU_UBO_BLOCK_NAME
void GPU_uniformbuf_debug_unbind_all()
void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
struct GPUShader GPUShader
#define output
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
ValueIterator values() const
Definition BLI_map.hh:846
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:241
int64_t size() const
Definition BLI_map.hh:927
virtual void compile(SVMCompiler &compiler)=0
constexpr StringRef drop_known_prefix(StringRef prefix) const
Result create_result(ResultType type, ResultPrecision precision)
Result & get_input(StringRef identifier) const
Definition operation.cc:144
Result & get_result(StringRef identifier)
Definition operation.cc:46
InputDescriptor & get_input_descriptor(StringRef identifier)
Definition operation.cc:164
void populate_result(StringRef identifier, Result result)
Definition operation.cc:154
void declare_input_descriptor(StringRef identifier, InputDescriptor descriptor)
Definition operation.cc:159
PixelOperation(Context &context, PixelCompileUnit &compile_unit, const Schedule &schedule)
Map< DOutputSocket, std::string > output_sockets_to_output_identifiers_map_
Map< std::string, DOutputSocket > inputs_to_linked_outputs_map_
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
Definition result.cc:264
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:253
ShaderOperation(Context &context, PixelCompileUnit &compile_unit, const Schedule &schedule)
local_group_size(16, 16) .push_constant(Type texture
T min(const T &a, const T &b)
bool is_output_linked_to_node_conditioned(DOutputSocket output, FunctionRef< bool(DNode)> condition)
Definition utilities.cc:78
static const char * glsl_swizzle_from_result_type(ResultType type)
static const char * get_set_function_name(ResultType type)
DOutputSocket get_output_linked_to_input(DInputSocket input)
Definition utilities.cc:47
VectorSet< DNode > Schedule
DOutputSocket find_preview_output_socket(const DNode &node)
Definition utilities.cc:161
ResultType get_node_socket_result_type(const bNodeSocket *socket)
Definition utilities.cc:63
InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket)
Definition utilities.cc:109
static const char * get_store_function_name(ResultType type)
static const char * glsl_store_expression_from_result_type(ResultType type)
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:131
static const char * glsl_type_from_result_type(ResultType type)
std::string composite
GPUShaderCreateInfo * create_info
GPUNodeLink * link
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Self & image(int slot, eGPUTextureFormat format, Qualifier qualifiers, ImageType type, StringRefNull name, Frequency freq=Frequency::PASS)
Self & compute_source(StringRefNull filename)
Self & auto_resource_location(bool value)
Self & typedef_source(StringRefNull filename)
Self & local_group_size(int local_size_x=-1, int local_size_y=-1, int local_size_z=-1)
Self & sampler(int slot, ImageType type, StringRefNull name, Frequency freq=Frequency::PASS, GPUSamplerState sampler=GPUSamplerState::internal_sampler())