This allows multiple threads to request different specializations without locking usage of all specialized shaders program when a new specialization is being compiled. The specialization constants are bundled in a structure that is being passed to the `Shader::bind()` method. The structure is owned by the calling thread and only used by the `Shader::bind()`. Only querying for the specialized shader (Map lookup) is locking the shader usage. The variant compilation is now also locking and ensured that multiple thread trying to compile the same variant will never result in race condition. Note that this removes the `is_dirty` optimization. This can be added back if this becomes a bottleneck in the future. Otherwise, the performance impact is not noticeable. Pull Request: https://projects.blender.org/blender/blender/pulls/136991
141 lines
3.8 KiB
C++
141 lines
3.8 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#include "testing/testing.h"
|
|
|
|
#include "GPU_batch.hh"
|
|
#include "GPU_capabilities.hh"
|
|
#include "GPU_compute.hh"
|
|
#include "GPU_context.hh"
|
|
#include "GPU_framebuffer.hh"
|
|
#include "GPU_shader.hh"
|
|
#include "GPU_state.hh"
|
|
#include "GPU_storage_buffer.hh"
|
|
#include "GPU_vertex_format.hh"
|
|
|
|
#include "BLI_vector.hh"
|
|
|
|
#include "gpu_shader_create_info.hh"
|
|
#include "gpu_shader_create_info_private.hh"
|
|
#include "gpu_testing.hh"
|
|
|
|
namespace blender::gpu::tests {
|
|
|
|
struct ShaderSpecializationConst {
|
|
GPUShader *shader = nullptr;
|
|
GPUStorageBuf *ssbo = nullptr;
|
|
Vector<int> data;
|
|
|
|
float float_in;
|
|
uint uint_in;
|
|
int int_in;
|
|
bool bool_in;
|
|
|
|
bool is_graphic = false;
|
|
|
|
ShaderSpecializationConst(const char *info_name)
|
|
{
|
|
GPU_render_begin();
|
|
|
|
this->init_shader(info_name);
|
|
|
|
GPU_storagebuf_bind(ssbo, GPU_shader_get_ssbo_binding(shader, "data_out"));
|
|
|
|
/* Test values. */
|
|
float_in = 52;
|
|
uint_in = 324;
|
|
int_in = 455;
|
|
bool_in = false;
|
|
|
|
int float_in_loc = GPU_shader_get_constant(shader, "float_in");
|
|
int uint_in_loc = GPU_shader_get_constant(shader, "uint_in");
|
|
int int_in_loc = GPU_shader_get_constant(shader, "int_in");
|
|
int bool_in_loc = GPU_shader_get_constant(shader, "bool_in");
|
|
|
|
shader::SpecializationConstants constants = GPU_shader_get_default_constant_state(shader);
|
|
constants.set_value(float_in_loc, float_in);
|
|
constants.set_value(uint_in_loc, uint_in);
|
|
constants.set_value(int_in_loc, int_in);
|
|
constants.set_value(bool_in_loc, bool_in);
|
|
|
|
this->validate(constants);
|
|
|
|
GPU_render_end();
|
|
}
|
|
|
|
~ShaderSpecializationConst()
|
|
{
|
|
if (shader != nullptr) {
|
|
GPU_shader_unbind();
|
|
GPU_shader_free(shader);
|
|
}
|
|
if (ssbo != nullptr) {
|
|
GPU_storagebuf_free(ssbo);
|
|
}
|
|
}
|
|
|
|
void init_shader(const char *info_name)
|
|
{
|
|
using namespace blender::gpu::shader;
|
|
|
|
uint data_len = 4;
|
|
ssbo = GPU_storagebuf_create_ex(data_len * sizeof(int), nullptr, GPU_USAGE_STREAM, __func__);
|
|
data.resize(data_len);
|
|
|
|
const GPUShaderCreateInfo *_info = gpu_shader_create_info_get(info_name);
|
|
const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(_info);
|
|
is_graphic = info.compute_source_.is_empty();
|
|
shader = GPU_shader_create_from_info_name(info_name);
|
|
EXPECT_NE(shader, nullptr);
|
|
}
|
|
|
|
void validate(shader::SpecializationConstants &constants)
|
|
{
|
|
if (is_graphic) {
|
|
GPUFrameBuffer *fb = GPU_framebuffer_create("test_fb");
|
|
GPU_framebuffer_default_size(fb, 1, 1);
|
|
GPU_framebuffer_bind(fb);
|
|
|
|
/* TODO(fclem): remove this boilerplate. */
|
|
GPUVertFormat format{};
|
|
GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U32, 1, GPU_FETCH_INT);
|
|
VertBuf *verts = GPU_vertbuf_create_with_format(format);
|
|
|
|
GPU_vertbuf_data_alloc(*verts, 1);
|
|
Batch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, verts, nullptr, GPU_BATCH_OWNS_VBO);
|
|
GPU_batch_set_shader(batch, shader, &constants);
|
|
GPU_batch_draw_advanced(batch, 0, 1, 0, 1);
|
|
GPU_batch_discard(batch);
|
|
|
|
GPU_framebuffer_free(fb);
|
|
}
|
|
else {
|
|
GPU_compute_dispatch(shader, 1, 1, 1, &constants);
|
|
}
|
|
|
|
GPU_finish();
|
|
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
|
|
GPU_storagebuf_read(ssbo, data.data());
|
|
|
|
EXPECT_EQ(data[0], int(float_in));
|
|
EXPECT_EQ(data[1], int(uint_in));
|
|
EXPECT_EQ(data[2], int(int_in));
|
|
EXPECT_EQ(data[3], int(bool_in));
|
|
}
|
|
};
|
|
|
|
static void test_specialization_constants_compute()
|
|
{
|
|
ShaderSpecializationConst("gpu_compute_specialization_test");
|
|
}
|
|
GPU_TEST(specialization_constants_compute)
|
|
|
|
static void test_specialization_constants_graphic()
|
|
{
|
|
ShaderSpecializationConst("gpu_graphic_specialization_test");
|
|
}
|
|
GPU_TEST(specialization_constants_graphic)
|
|
|
|
} // namespace blender::gpu::tests
|