WIP: Vulkan: Initial Immediate Mode Support. #106954
|
@ -1235,7 +1235,7 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf)
|
|||
|
||||
char err_out[256] = "unknown";
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(
|
||||
tgpf->sizex, tgpf->sizey, true, GPU_RGBA8, err_out);
|
||||
tgpf->sizex, tgpf->sizey, true, GPU_RGBA8, GPU_TEXTURE_USAGE_HOST_READ, err_out);
|
||||
if (offscreen == NULL) {
|
||||
printf("GPencil - Fill - Unable to create fill buffer\n");
|
||||
return false;
|
||||
|
|
|
@ -757,7 +757,12 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
|
|||
|
||||
/* corrects render size with actual size, not every card supports non-power-of-two dimensions */
|
||||
DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */
|
||||
ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA16F, err_out);
|
||||
ofs = GPU_offscreen_create(sizex,
|
||||
sizey,
|
||||
true,
|
||||
GPU_RGBA16F,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_HOST_READ,
|
||||
err_out);
|
||||
DRW_opengl_context_disable();
|
||||
|
||||
if (!ofs) {
|
||||
|
|
|
@ -425,7 +425,13 @@ static void screen_preview_draw(const bScreen *screen, int size_x, int size_y)
|
|||
void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uint *r_rect)
|
||||
{
|
||||
char err_out[256] = "unknown";
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, GPU_RGBA8, err_out);
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(size_x,
|
||||
size_y,
|
||||
true,
|
||||
GPU_RGBA8,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ |
|
||||
GPU_TEXTURE_USAGE_HOST_READ,
|
||||
err_out);
|
||||
|
||||
GPU_offscreen_bind(offscreen, true);
|
||||
GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
|
|
@ -1886,7 +1886,12 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph,
|
|||
|
||||
if (own_ofs) {
|
||||
/* bind */
|
||||
ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA8, err_out);
|
||||
ofs = GPU_offscreen_create(sizex,
|
||||
sizey,
|
||||
true,
|
||||
GPU_RGBA8,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_HOST_READ,
|
||||
err_out);
|
||||
if (ofs == nullptr) {
|
||||
DRW_opengl_context_disable();
|
||||
return nullptr;
|
||||
|
|
|
@ -211,9 +211,11 @@ set(VULKAN_SRC
|
|||
vulkan/vk_drawlist.cc
|
||||
vulkan/vk_fence.cc
|
||||
vulkan/vk_framebuffer.cc
|
||||
vulkan/vk_immediate.cc
|
||||
vulkan/vk_index_buffer.cc
|
||||
vulkan/vk_memory.cc
|
||||
vulkan/vk_memory_layout.cc
|
||||
vulkan/vk_pipeline_state.cc
|
||||
vulkan/vk_pipeline.cc
|
||||
vulkan/vk_pixel_buffer.cc
|
||||
vulkan/vk_push_constants.cc
|
||||
|
@ -226,6 +228,8 @@ set(VULKAN_SRC
|
|||
vulkan/vk_storage_buffer.cc
|
||||
vulkan/vk_texture.cc
|
||||
vulkan/vk_uniform_buffer.cc
|
||||
vulkan/vk_vertex_attribute_object_cache.cc
|
||||
vulkan/vk_vertex_attribute_object.cc
|
||||
vulkan/vk_vertex_buffer.cc
|
||||
|
||||
vulkan/vk_backend.hh
|
||||
|
@ -240,9 +244,11 @@ set(VULKAN_SRC
|
|||
vulkan/vk_drawlist.hh
|
||||
vulkan/vk_fence.hh
|
||||
vulkan/vk_framebuffer.hh
|
||||
vulkan/vk_immediate.hh
|
||||
vulkan/vk_index_buffer.hh
|
||||
vulkan/vk_memory.hh
|
||||
vulkan/vk_memory_layout.hh
|
||||
vulkan/vk_pipeline_state.hh
|
||||
vulkan/vk_pipeline.hh
|
||||
vulkan/vk_pixel_buffer.hh
|
||||
vulkan/vk_push_constants.hh
|
||||
|
@ -255,6 +261,8 @@ set(VULKAN_SRC
|
|||
vulkan/vk_storage_buffer.hh
|
||||
vulkan/vk_texture.hh
|
||||
vulkan/vk_uniform_buffer.hh
|
||||
vulkan/vk_vertex_attribute_object_cache.hh
|
||||
vulkan/vk_vertex_attribute_object.hh
|
||||
vulkan/vk_vertex_buffer.hh
|
||||
)
|
||||
|
||||
|
@ -827,8 +835,11 @@ if(WITH_GTESTS)
|
|||
set(TEST_SRC
|
||||
tests/gpu_testing.cc
|
||||
|
||||
tests/state_blend_test.cc
|
||||
tests/framebuffer_test.cc
|
||||
tests/immediate_test.cc
|
||||
tests/index_buffer_test.cc
|
||||
tests/offscreen_test.cc
|
||||
tests/push_constants_test.cc
|
||||
tests/shader_test.cc
|
||||
tests/storage_buffer_test.cc
|
||||
|
|
|
@ -588,9 +588,14 @@ typedef struct GPUOffScreen GPUOffScreen;
|
|||
* \a format is the format of the color buffer.
|
||||
* If \a err_out is not `nullptr` it will be use to write any configuration error message..
|
||||
* \note This function binds the framebuffer to the active context.
|
||||
* \note `GPU_TEXTURE_USAGE_ATTACHMENT` is added to the usage parameter by default.
|
||||
*/
|
||||
GPUOffScreen *GPU_offscreen_create(
|
||||
int width, int height, bool with_depth_buffer, eGPUTextureFormat format, char err_out[256]);
|
||||
GPUOffScreen *GPU_offscreen_create(int width,
|
||||
int height,
|
||||
bool with_depth_buffer,
|
||||
eGPUTextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
char err_out[256]);
|
||||
|
||||
/**
|
||||
* Free a #GPUOffScreen.
|
||||
|
|
|
@ -652,8 +652,12 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs)
|
|||
return gpu_offscreen_fb_get(ofs);
|
||||
}
|
||||
|
||||
GPUOffScreen *GPU_offscreen_create(
|
||||
int width, int height, bool depth, eGPUTextureFormat format, char err_out[256])
|
||||
GPUOffScreen *GPU_offscreen_create(int width,
|
||||
int height,
|
||||
bool depth,
|
||||
eGPUTextureFormat format,
|
||||
const eGPUTextureUsage usage_,
|
||||
char err_out[256])
|
||||
{
|
||||
GPUOffScreen *ofs = MEM_cnew<GPUOffScreen>(__func__);
|
||||
|
||||
|
@ -662,7 +666,7 @@ GPUOffScreen *GPU_offscreen_create(
|
|||
height = max_ii(1, height);
|
||||
width = max_ii(1, width);
|
||||
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
|
||||
const eGPUTextureUsage usage = usage_ | GPU_TEXTURE_USAGE_ATTACHMENT;
|
||||
ofs->color = GPU_texture_create_2d("ofs_color", width, height, 1, format, usage, nullptr);
|
||||
|
||||
if (depth) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_immediate.h"
|
||||
#include "GPU_shader_builtin.h"
|
||||
#include "gpu_testing.hh"
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
namespace blender::gpu::tests {
|
||||
|
||||
static constexpr int Size = 10;
|
||||
|
||||
static void test_immediate_crosshair()
|
||||
{
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(Size,
|
||||
Size,
|
||||
false,
|
||||
GPU_RGBA16F,
|
||||
GPU_TEXTURE_USAGE_ATTACHMENT |
|
||||
GPU_TEXTURE_USAGE_HOST_READ,
|
||||
nullptr);
|
||||
BLI_assert(offscreen != nullptr);
|
||||
GPU_offscreen_bind(offscreen, false);
|
||||
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
float2 viewport_size(Size);
|
||||
float4 color(1.0, 0.5, 0.25, 1.0);
|
||||
// immUniform2fv("viewportSize", viewport_size);
|
||||
// immUniform1f("lineWidth", 1.0f);
|
||||
immUniformColor4fv(color);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 4);
|
||||
immVertex3f(pos, -1.0f, 0.0f, 0.0f);
|
||||
immVertex3f(pos, 1.0f, 0.0f, 0.0f);
|
||||
immVertex3f(pos, 0.0f, -1.0f, 0.0f);
|
||||
immVertex3f(pos, 0.0f, 1.0f, 0.0f);
|
||||
immEnd();
|
||||
|
||||
GPU_offscreen_unbind(offscreen, false);
|
||||
GPU_flush();
|
||||
|
||||
GPU_offscreen_free(offscreen);
|
||||
}
|
||||
GPU_TEST(immediate_crosshair)
|
||||
|
||||
} // namespace blender::gpu::tests
|
|
@ -0,0 +1,84 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "gpu_testing.hh"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_batch_presets.h"
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_matrix.h"
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
#include "intern/draw_cache.h"
|
||||
|
||||
namespace blender::gpu::tests {
|
||||
const size_t Size = 512;
|
||||
|
||||
static void test_offscreen_draw_batch_quad()
|
||||
{
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(Size,
|
||||
Size,
|
||||
false,
|
||||
GPU_RGBA16F,
|
||||
GPU_TEXTURE_USAGE_ATTACHMENT |
|
||||
GPU_TEXTURE_USAGE_HOST_READ,
|
||||
nullptr);
|
||||
BLI_assert(offscreen != nullptr);
|
||||
GPU_offscreen_bind(offscreen, false);
|
||||
/*
|
||||
GPUTexture *color_texture = GPU_offscreen_color_texture(offscreen);
|
||||
float4 clear_color(0.0f);
|
||||
GPU_texture_clear(color_texture, GPU_DATA_FLOAT, clear_color);
|
||||
*/
|
||||
|
||||
GPUBatch *batch = DRW_cache_quad_get();
|
||||
float4 color(1.0f, 0.5f, 0.0f, 1.0f);
|
||||
|
||||
GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
GPU_batch_uniform_4fv(batch, "color", color);
|
||||
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_scale_1f(0.5f);
|
||||
GPU_matrix_rotate_2d(45.0f);
|
||||
|
||||
GPU_batch_draw(batch);
|
||||
GPU_offscreen_unbind(offscreen, false);
|
||||
GPU_flush();
|
||||
GPU_matrix_pop();
|
||||
|
||||
GPU_offscreen_free(offscreen);
|
||||
DRW_shape_cache_free();
|
||||
}
|
||||
GPU_TEST(offscreen_draw_batch_quad);
|
||||
|
||||
static void test_offscreen_draw_batch_sphere()
|
||||
{
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(
|
||||
Size, Size, false, GPU_RGBA16F, GPU_TEXTURE_USAGE_ATTACHMENT, nullptr);
|
||||
BLI_assert(offscreen != nullptr);
|
||||
GPU_offscreen_bind(offscreen, false);
|
||||
|
||||
GPUBatch *batch = DRW_cache_sphere_get(DRW_LOD_MEDIUM);
|
||||
float4 color(1.0f, 0.5f, 0.0f, 0.8f);
|
||||
|
||||
GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
GPU_batch_uniform_4fv(batch, "color", color);
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_scale_1f(0.5f);
|
||||
GPU_matrix_rotate_2d(45.0f);
|
||||
|
||||
GPU_batch_draw(batch);
|
||||
GPU_offscreen_unbind(offscreen, false);
|
||||
GPU_flush();
|
||||
GPU_matrix_pop();
|
||||
|
||||
GPU_offscreen_free(offscreen);
|
||||
DRW_shape_cache_free();
|
||||
}
|
||||
GPU_TEST(offscreen_draw_batch_sphere);
|
||||
|
||||
} // namespace blender::gpu::tests
|
|
@ -0,0 +1,132 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "gpu_testing.hh"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_batch_presets.h"
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_matrix.h"
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
#include "intern/draw_cache.h"
|
||||
|
||||
namespace blender::gpu::tests {
|
||||
|
||||
template<eGPUBlend blend_type>
|
||||
void blend_test(float4 source_a, float4 source_b, float4 expected_result)
|
||||
{
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(1,
|
||||
1,
|
||||
false,
|
||||
GPU_RGBA16F,
|
||||
GPU_TEXTURE_USAGE_ATTACHMENT |
|
||||
GPU_TEXTURE_USAGE_HOST_READ,
|
||||
nullptr);
|
||||
BLI_assert(offscreen != nullptr);
|
||||
GPU_offscreen_bind(offscreen, false);
|
||||
GPUTexture *color_texture = GPU_offscreen_color_texture(offscreen);
|
||||
GPU_texture_clear(color_texture, GPU_DATA_FLOAT, source_a);
|
||||
|
||||
GPUBatch *batch = DRW_cache_quad_get();
|
||||
|
||||
GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
GPU_batch_uniform_4fv(batch, "color", source_b);
|
||||
GPU_blend(blend_type);
|
||||
|
||||
GPU_batch_draw(batch);
|
||||
GPU_offscreen_unbind(offscreen, false);
|
||||
GPU_flush();
|
||||
|
||||
float4 read_back;
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
|
||||
GPU_offscreen_read_pixels(offscreen, GPU_DATA_FLOAT, &read_back);
|
||||
EXPECT_EQ(read_back, expected_result);
|
||||
|
||||
GPU_offscreen_free(offscreen);
|
||||
DRW_shape_cache_free();
|
||||
}
|
||||
|
||||
static void test_blend_none()
|
||||
{
|
||||
blend_test<GPU_BLEND_NONE>(float4(1.0f, 0.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f));
|
||||
}
|
||||
GPU_TEST(blend_none)
|
||||
|
||||
static void test_blend_alpha()
|
||||
{
|
||||
blend_test<GPU_BLEND_ALPHA>(float4(1.0f, 0.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(0.5f, 0.5f, 0.5f, 1.0f));
|
||||
}
|
||||
GPU_TEST(blend_alpha)
|
||||
|
||||
static void test_blend_alpha_premult()
|
||||
{
|
||||
blend_test<GPU_BLEND_ALPHA_PREMULT>(float4(1.0f, 0.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(0.5f, 1.0f, 0.5f, 1.0f));
|
||||
}
|
||||
GPU_TEST(blend_alpha_premult)
|
||||
|
||||
static void test_blend_additive()
|
||||
{
|
||||
blend_test<GPU_BLEND_ADDITIVE>(float4(1.0f, 0.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(1.0f, 0.5f, 1.0f, 1.0f));
|
||||
}
|
||||
GPU_TEST(blend_additive)
|
||||
|
||||
static void test_blend_additive_premult()
|
||||
{
|
||||
blend_test<GPU_BLEND_ADDITIVE_PREMULT>(float4(1.0f, 0.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(1.0f, 1.0f, 1.0f, 1.5f));
|
||||
}
|
||||
GPU_TEST(blend_additive_premult)
|
||||
|
||||
static void test_blend_multiply()
|
||||
{
|
||||
blend_test<GPU_BLEND_MULTIPLY>(float4(1.0f, 0.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
}
|
||||
GPU_TEST(blend_multiply)
|
||||
|
||||
static void test_blend_subtract()
|
||||
{
|
||||
blend_test<GPU_BLEND_SUBTRACT>(float4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(1.0f, 0.0f, 1.0f, 0.5f));
|
||||
}
|
||||
GPU_TEST(blend_subtract)
|
||||
|
||||
static void test_blend_invert()
|
||||
{
|
||||
blend_test<GPU_BLEND_INVERT>(float4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
GPU_TEST(blend_invert)
|
||||
|
||||
static void test_blend_oit()
|
||||
{
|
||||
blend_test<GPU_BLEND_OIT>(float4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(1.0f, 2.0f, 1.0f, 0.5f));
|
||||
}
|
||||
GPU_TEST(blend_oit)
|
||||
|
||||
static void test_blend_background()
|
||||
{
|
||||
blend_test<GPU_BLEND_BACKGROUND>(float4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||
float4(0.5f, 0.5f, 0.5f, 0.5f));
|
||||
}
|
||||
GPU_TEST(blend_background)
|
||||
|
||||
} // namespace blender::gpu::tests
|
|
@ -7,9 +7,34 @@
|
|||
|
||||
#include "vk_batch.hh"
|
||||
|
||||
#include "vk_context.hh"
|
||||
#include "vk_index_buffer.hh"
|
||||
#include "vk_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
void VKBatch::draw(int /*v_first*/, int /*v_count*/, int /*i_first*/, int /*i_count*/) {}
|
||||
void VKBatch::draw(int v_first, int v_count, int i_first, int i_count)
|
||||
{
|
||||
if (flag & GPU_BATCH_DIRTY) {
|
||||
vao_cache_.clear();
|
||||
flag &= ~GPU_BATCH_DIRTY;
|
||||
}
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
context.state_manager->apply_state();
|
||||
VKVertexAttributeObject &vao = vao_cache_.vao_get(this);
|
||||
vao.update_bindings(context, *this);
|
||||
context.bind_graphics_pipeline(prim_type, vao);
|
||||
vao.bind(context);
|
||||
|
||||
VKIndexBuffer *index_buffer = index_buffer_get();
|
||||
if (index_buffer) {
|
||||
index_buffer->upload_data();
|
||||
index_buffer->bind(context);
|
||||
}
|
||||
|
||||
context.command_buffer_get().draw(v_first, v_count, i_first, i_count);
|
||||
}
|
||||
|
||||
void VKBatch::draw_indirect(GPUStorageBuf * /*indirect_buf*/, intptr_t /*offset*/) {}
|
||||
|
||||
|
@ -20,4 +45,19 @@ void VKBatch::multi_draw_indirect(GPUStorageBuf * /*indirect_buf*/,
|
|||
{
|
||||
}
|
||||
|
||||
VKVertexBuffer *VKBatch::vertex_buffer_get(int index)
|
||||
{
|
||||
return unwrap(verts_(index));
|
||||
}
|
||||
|
||||
VKVertexBuffer *VKBatch::instance_buffer_get(int index)
|
||||
{
|
||||
return unwrap(inst_(index));
|
||||
}
|
||||
|
||||
VKIndexBuffer *VKBatch::index_buffer_get()
|
||||
{
|
||||
return unwrap(unwrap(elem));
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -9,9 +9,14 @@
|
|||
|
||||
#include "gpu_batch_private.hh"
|
||||
|
||||
#include "vk_vertex_attribute_object_cache.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class VKBatch : public Batch {
|
||||
private:
|
||||
VKVaoCache vao_cache_;
|
||||
|
||||
public:
|
||||
void draw(int v_first, int v_count, int i_first, int i_count) override;
|
||||
void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) override;
|
||||
|
@ -19,6 +24,10 @@ class VKBatch : public Batch {
|
|||
int count,
|
||||
intptr_t offset,
|
||||
intptr_t stride) override;
|
||||
|
||||
VKVertexBuffer *vertex_buffer_get(int index);
|
||||
VKVertexBuffer *instance_buffer_get(int index);
|
||||
VKIndexBuffer *index_buffer_get();
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
#include "vk_buffer.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_index_buffer.hh"
|
||||
#include "vk_memory.hh"
|
||||
#include "vk_pipeline.hh"
|
||||
#include "vk_texture.hh"
|
||||
#include "vk_vertex_buffer.hh"
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
|
@ -74,19 +76,43 @@ void VKCommandBuffer::bind(const VKDescriptorSet &descriptor_set,
|
|||
vk_command_buffer_, bind_point, vk_pipeline_layout, 0, 1, &vk_descriptor_set, 0, 0);
|
||||
}
|
||||
|
||||
void VKCommandBuffer::begin_render_pass(const VKFrameBuffer &framebuffer)
|
||||
void VKCommandBuffer::bind(const uint32_t binding,
|
||||
const VKVertexBuffer &vertex_buffer,
|
||||
const VkDeviceSize offset)
|
||||
{
|
||||
VkRenderPassBeginInfo render_pass_begin_info = {};
|
||||
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
render_pass_begin_info.renderPass = framebuffer.vk_render_pass_get();
|
||||
render_pass_begin_info.framebuffer = framebuffer.vk_framebuffer_get();
|
||||
render_pass_begin_info.renderArea = framebuffer.vk_render_area_get();
|
||||
vkCmdBeginRenderPass(vk_command_buffer_, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
|
||||
bind(binding, vertex_buffer.vk_handle(), offset);
|
||||
}
|
||||
|
||||
void VKCommandBuffer::end_render_pass(const VKFrameBuffer & /*framebuffer*/)
|
||||
void VKCommandBuffer::bind(const uint32_t binding,
|
||||
const VkBuffer &vk_buffer,
|
||||
const VkDeviceSize offset)
|
||||
{
|
||||
vkCmdEndRenderPass(vk_command_buffer_);
|
||||
validate_framebuffer_exists();
|
||||
ensure_active_framebuffer();
|
||||
vkCmdBindVertexBuffers(vk_command_buffer_, binding, 1, &vk_buffer, &offset);
|
||||
}
|
||||
|
||||
void VKCommandBuffer::bind(const VKIndexBuffer &index_buffer, VkIndexType index_type)
|
||||
{
|
||||
validate_framebuffer_exists();
|
||||
ensure_active_framebuffer();
|
||||
VkBuffer vk_buffer = index_buffer.vk_handle();
|
||||
vkCmdBindIndexBuffer(vk_command_buffer_, vk_buffer, 0, index_type);
|
||||
}
|
||||
|
||||
void VKCommandBuffer::begin_render_pass(const VKFrameBuffer &framebuffer)
|
||||
{
|
||||
validate_framebuffer_not_exists();
|
||||
state.framebuffer_ = &framebuffer;
|
||||
}
|
||||
|
||||
void VKCommandBuffer::end_render_pass(const VKFrameBuffer &framebuffer)
|
||||
{
|
||||
UNUSED_VARS_NDEBUG(framebuffer)
|
||||
validate_framebuffer_exists();
|
||||
BLI_assert(state.framebuffer_ == &framebuffer);
|
||||
ensure_no_active_framebuffer();
|
||||
state.framebuffer_ = nullptr;
|
||||
}
|
||||
|
||||
void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
|
||||
|
@ -105,6 +131,7 @@ void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
|
|||
|
||||
void VKCommandBuffer::fill(VKBuffer &buffer, uint32_t clear_data)
|
||||
{
|
||||
ensure_no_active_framebuffer();
|
||||
vkCmdFillBuffer(vk_command_buffer_, buffer.vk_handle(), 0, buffer.size_in_bytes(), clear_data);
|
||||
}
|
||||
|
||||
|
@ -112,6 +139,7 @@ void VKCommandBuffer::copy(VKBuffer &dst_buffer,
|
|||
VKTexture &src_texture,
|
||||
Span<VkBufferImageCopy> regions)
|
||||
{
|
||||
ensure_no_active_framebuffer();
|
||||
vkCmdCopyImageToBuffer(vk_command_buffer_,
|
||||
src_texture.vk_image_handle(),
|
||||
src_texture.current_layout_get(),
|
||||
|
@ -123,6 +151,7 @@ void VKCommandBuffer::copy(VKTexture &dst_texture,
|
|||
VKBuffer &src_buffer,
|
||||
Span<VkBufferImageCopy> regions)
|
||||
{
|
||||
ensure_no_active_framebuffer();
|
||||
vkCmdCopyBufferToImage(vk_command_buffer_,
|
||||
src_buffer.vk_handle(),
|
||||
dst_texture.vk_image_handle(),
|
||||
|
@ -136,6 +165,7 @@ void VKCommandBuffer::clear(VkImage vk_image,
|
|||
const VkClearColorValue &vk_clear_color,
|
||||
Span<VkImageSubresourceRange> ranges)
|
||||
{
|
||||
ensure_no_active_framebuffer();
|
||||
vkCmdClearColorImage(vk_command_buffer_,
|
||||
vk_image,
|
||||
vk_image_layout,
|
||||
|
@ -146,13 +176,25 @@ void VKCommandBuffer::clear(VkImage vk_image,
|
|||
|
||||
void VKCommandBuffer::clear(Span<VkClearAttachment> attachments, Span<VkClearRect> areas)
|
||||
{
|
||||
validate_framebuffer_exists();
|
||||
ensure_active_framebuffer();
|
||||
vkCmdClearAttachments(
|
||||
vk_command_buffer_, attachments.size(), attachments.data(), areas.size(), areas.data());
|
||||
}
|
||||
|
||||
void VKCommandBuffer::draw(int v_first, int v_count, int i_first, int i_count)
|
||||
{
|
||||
validate_framebuffer_exists();
|
||||
ensure_active_framebuffer();
|
||||
vkCmdDraw(vk_command_buffer_, v_count, i_count, v_first, i_first);
|
||||
}
|
||||
|
||||
void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages,
|
||||
VkPipelineStageFlags destination_stages)
|
||||
{
|
||||
if (state.framebuffer_) {
|
||||
ensure_active_framebuffer();
|
||||
}
|
||||
vkCmdPipelineBarrier(vk_command_buffer_,
|
||||
source_stages,
|
||||
destination_stages,
|
||||
|
@ -167,6 +209,7 @@ void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages,
|
|||
|
||||
void VKCommandBuffer::pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers)
|
||||
{
|
||||
ensure_no_active_framebuffer();
|
||||
vkCmdPipelineBarrier(vk_command_buffer_,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
|
@ -181,11 +224,13 @@ void VKCommandBuffer::pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_b
|
|||
|
||||
void VKCommandBuffer::dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
|
||||
{
|
||||
ensure_no_active_framebuffer();
|
||||
vkCmdDispatch(vk_command_buffer_, groups_x_len, groups_y_len, groups_z_len);
|
||||
}
|
||||
|
||||
void VKCommandBuffer::submit()
|
||||
{
|
||||
ensure_no_active_framebuffer();
|
||||
end_recording();
|
||||
encode_recorded_commands();
|
||||
submit_encoded_commands();
|
||||
|
@ -210,4 +255,47 @@ void VKCommandBuffer::submit_encoded_commands()
|
|||
submission_id_.next();
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Framebuffer/RenderPass state tracking
|
||||
* \{ */
|
||||
|
||||
void VKCommandBuffer::validate_framebuffer_not_exists()
|
||||
{
|
||||
BLI_assert_msg(state.framebuffer_ == nullptr && state.framebuffer_active_ == false,
|
||||
"State error: expected no framebuffer being tracked.");
|
||||
}
|
||||
|
||||
void VKCommandBuffer::validate_framebuffer_exists()
|
||||
{
|
||||
BLI_assert_msg(state.framebuffer_, "State error: expected framebuffer being tracked.");
|
||||
}
|
||||
|
||||
void VKCommandBuffer::ensure_no_active_framebuffer()
|
||||
{
|
||||
state.checks_++;
|
||||
if (state.framebuffer_ && state.framebuffer_active_) {
|
||||
vkCmdEndRenderPass(vk_command_buffer_);
|
||||
state.framebuffer_active_ = false;
|
||||
state.switches_++;
|
||||
}
|
||||
}
|
||||
|
||||
void VKCommandBuffer::ensure_active_framebuffer()
|
||||
{
|
||||
BLI_assert(state.framebuffer_);
|
||||
state.checks_++;
|
||||
if (!state.framebuffer_active_) {
|
||||
VkRenderPassBeginInfo render_pass_begin_info = {};
|
||||
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
render_pass_begin_info.renderPass = state.framebuffer_->vk_render_pass_get();
|
||||
render_pass_begin_info.framebuffer = state.framebuffer_->vk_framebuffer_get();
|
||||
render_pass_begin_info.renderArea = state.framebuffer_->vk_render_area_get();
|
||||
vkCmdBeginRenderPass(vk_command_buffer_, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
|
||||
state.framebuffer_active_ = true;
|
||||
state.switches_++;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -16,9 +16,11 @@ namespace blender::gpu {
|
|||
class VKBuffer;
|
||||
class VKDescriptorSet;
|
||||
class VKFrameBuffer;
|
||||
class VKIndexBuffer;
|
||||
class VKPipeline;
|
||||
class VKPushConstants;
|
||||
class VKTexture;
|
||||
class VKVertexBuffer;
|
||||
|
||||
/** Command buffer to keep track of the life-time of a command buffer. */
|
||||
class VKCommandBuffer : NonCopyable, NonMovable {
|
||||
|
@ -31,6 +33,42 @@ class VKCommandBuffer : NonCopyable, NonMovable {
|
|||
VkFence vk_fence_ = VK_NULL_HANDLE;
|
||||
VKSubmissionID submission_id_;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Some vulkan command require an active frame buffer. Others require no active frame-buffer. As
|
||||
* our current API does not provide a solution for this we need to keep track of the actual state
|
||||
* and do the changes when recording the next command.
|
||||
*
|
||||
* This is a temporary solution to get things rolling.
|
||||
* TODO: In a future solution we should decide the scope of a command buffer.
|
||||
*
|
||||
* - command buffer per draw command.
|
||||
* - minimize command buffers and track render passes.
|
||||
* - add custom encoder to also track resource usages.
|
||||
*
|
||||
* Currently I expect the custom encoder has to be done eventually. But want to keep post-poning
|
||||
* the custom encoder for now to collect more use cases it should solve. (first pixel drawn on
|
||||
* screen).
|
||||
*
|
||||
* Some command can also be encoded in another way when encoded as a first command. For example
|
||||
* clearing a framebuffer textures isn't allowed inside a render pass, but clearing the
|
||||
* framebuffer textures via ops is allowed. When clearing a framebuffer texture directly after
|
||||
* beginning a render pass could be re-encoded to do this in the same command.
|
||||
*
|
||||
* So for now we track the state and temporary switch to another state if the command requires
|
||||
* it.
|
||||
*/
|
||||
struct {
|
||||
/* Reference to the last_framebuffer where begin_render_pass was called for. */
|
||||
const VKFrameBuffer *framebuffer_ = nullptr;
|
||||
/* Is last_framebuffer_ currently bound. Each call should ensure the correct state. */
|
||||
bool framebuffer_active_ = false;
|
||||
/* Amount of times a check has been requested. */
|
||||
uint64_t checks_ = 0;
|
||||
/* Amount of times a check required to change the render pass. */
|
||||
uint64_t switches_ = 0;
|
||||
} state;
|
||||
|
||||
public:
|
||||
virtual ~VKCommandBuffer();
|
||||
void init(const VkDevice vk_device, const VkQueue vk_queue, VkCommandBuffer vk_command_buffer);
|
||||
|
@ -40,6 +78,12 @@ class VKCommandBuffer : NonCopyable, NonMovable {
|
|||
void bind(const VKDescriptorSet &descriptor_set,
|
||||
const VkPipelineLayout vk_pipeline_layout,
|
||||
VkPipelineBindPoint bind_point);
|
||||
void bind(const uint32_t binding,
|
||||
const VKVertexBuffer &vertex_buffer,
|
||||
const VkDeviceSize offset);
|
||||
/* Bind the given buffer as a vertex buffer. */
|
||||
void bind(const uint32_t binding, const VkBuffer &buffer, const VkDeviceSize offset);
|
||||
void bind(const VKIndexBuffer &index_buffer, VkIndexType index_type);
|
||||
void begin_render_pass(const VKFrameBuffer &framebuffer);
|
||||
void end_render_pass(const VKFrameBuffer &framebuffer);
|
||||
|
||||
|
@ -72,6 +116,8 @@ class VKCommandBuffer : NonCopyable, NonMovable {
|
|||
void clear(Span<VkClearAttachment> attachments, Span<VkClearRect> areas);
|
||||
void fill(VKBuffer &buffer, uint32_t data);
|
||||
|
||||
void draw(int v_first, int v_count, int i_first, int i_count);
|
||||
|
||||
/**
|
||||
* Stop recording commands, encode + send the recordings to Vulkan, wait for the until the
|
||||
* commands have been executed and start the command buffer to accept recordings again.
|
||||
|
@ -86,6 +132,30 @@ class VKCommandBuffer : NonCopyable, NonMovable {
|
|||
private:
|
||||
void encode_recorded_commands();
|
||||
void submit_encoded_commands();
|
||||
|
||||
/**
|
||||
* Validate that there isn't a framebuffer being tracked (bound or not bound).
|
||||
*
|
||||
* Raises an assert in debug when a framebuffer is being tracked.
|
||||
*/
|
||||
void validate_framebuffer_not_exists();
|
||||
|
||||
/**
|
||||
* Validate that there is a framebuffer being tracked (bound or not bound).
|
||||
*
|
||||
* Raises an assert in debug when no framebuffer is being tracked.
|
||||
*/
|
||||
void validate_framebuffer_exists();
|
||||
|
||||
/**
|
||||
* Ensure that there is no framebuffer being tracked or the tracked framebuffer isn't bound.
|
||||
*/
|
||||
void ensure_no_active_framebuffer();
|
||||
|
||||
/**
|
||||
* Ensure that the tracked framebuffer is bound.
|
||||
*/
|
||||
void ensure_active_framebuffer();
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -243,6 +243,127 @@ VkFormat to_vk_format(const eGPUTextureFormat format)
|
|||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size)
|
||||
{
|
||||
switch (type) {
|
||||
case GPU_COMP_I8:
|
||||
switch (size) {
|
||||
case 1:
|
||||
return VK_FORMAT_R8_SNORM;
|
||||
case 2:
|
||||
return VK_FORMAT_R8G8_SNORM;
|
||||
case 3:
|
||||
return VK_FORMAT_R8G8B8_SNORM;
|
||||
case 4:
|
||||
return VK_FORMAT_R8G8B8A8_SNORM;
|
||||
default:
|
||||
BLI_assert(false);
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
case GPU_COMP_U8:
|
||||
switch (size) {
|
||||
case 1:
|
||||
return VK_FORMAT_R8_UNORM;
|
||||
case 2:
|
||||
return VK_FORMAT_R8G8_UNORM;
|
||||
case 3:
|
||||
return VK_FORMAT_R8G8B8_UNORM;
|
||||
case 4:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
default:
|
||||
BLI_assert(false);
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
case GPU_COMP_I16:
|
||||
switch (size) {
|
||||
case 2:
|
||||
return VK_FORMAT_R16_SNORM;
|
||||
case 4:
|
||||
return VK_FORMAT_R16G16_SNORM;
|
||||
case 6:
|
||||
return VK_FORMAT_R16G16B16_SNORM;
|
||||
case 8:
|
||||
return VK_FORMAT_R16G16B16A16_SNORM;
|
||||
default:
|
||||
BLI_assert(false);
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
case GPU_COMP_U16:
|
||||
switch (size) {
|
||||
case 2:
|
||||
return VK_FORMAT_R16_UNORM;
|
||||
case 4:
|
||||
return VK_FORMAT_R16G16_UNORM;
|
||||
case 6:
|
||||
return VK_FORMAT_R16G16B16_UNORM;
|
||||
case 8:
|
||||
return VK_FORMAT_R16G16B16A16_UNORM;
|
||||
default:
|
||||
BLI_assert(false);
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
case GPU_COMP_I32:
|
||||
switch (size) {
|
||||
case 4:
|
||||
return VK_FORMAT_R32_SINT;
|
||||
case 8:
|
||||
return VK_FORMAT_R32G32_SINT;
|
||||
case 12:
|
||||
return VK_FORMAT_R32G32B32_SINT;
|
||||
case 16:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
default:
|
||||
BLI_assert(false);
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
case GPU_COMP_U32:
|
||||
switch (size) {
|
||||
case 4:
|
||||
return VK_FORMAT_R32_UINT;
|
||||
case 8:
|
||||
return VK_FORMAT_R32G32_UINT;
|
||||
case 12:
|
||||
return VK_FORMAT_R32G32B32_UINT;
|
||||
case 16:
|
||||
return VK_FORMAT_R32G32B32A32_UINT;
|
||||
default:
|
||||
BLI_assert(false);
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
case GPU_COMP_F32:
|
||||
switch (size) {
|
||||
case 4:
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
case 8:
|
||||
return VK_FORMAT_R32G32_SFLOAT;
|
||||
case 12:
|
||||
return VK_FORMAT_R32G32B32_SFLOAT;
|
||||
case 16:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
case 64:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
default:
|
||||
BLI_assert(false);
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
|
||||
case GPU_COMP_I10:
|
||||
BLI_assert(size == 4);
|
||||
// return VK_FORMAT_A2B10G10R10_SINT_PACK32;
|
||||
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
||||
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return VK_FORMAT_R32_SFLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
VkImageType to_vk_image_type(const eGPUTextureType type)
|
||||
{
|
||||
switch (type) {
|
||||
|
@ -349,4 +470,63 @@ VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const voi
|
|||
return result;
|
||||
}
|
||||
|
||||
VkIndexType to_vk_index_type(const GPUIndexBufType index_type)
|
||||
{
|
||||
switch (index_type) {
|
||||
case GPU_INDEX_U16:
|
||||
return VK_INDEX_TYPE_UINT16;
|
||||
case GPU_INDEX_U32:
|
||||
return VK_INDEX_TYPE_UINT32;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
return VK_INDEX_TYPE_UINT16;
|
||||
}
|
||||
|
||||
VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type)
|
||||
{
|
||||
switch (prim_type) {
|
||||
case GPU_PRIM_POINTS:
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
case GPU_PRIM_LINES:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
case GPU_PRIM_TRIS:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
case GPU_PRIM_LINE_STRIP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
case GPU_PRIM_LINE_LOOP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
|
||||
case GPU_PRIM_TRI_STRIP:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||
case GPU_PRIM_TRI_FAN:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
|
||||
case GPU_PRIM_LINES_ADJ:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
|
||||
case GPU_PRIM_TRIS_ADJ:
|
||||
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
|
||||
case GPU_PRIM_LINE_STRIP_ADJ:
|
||||
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
|
||||
|
||||
case GPU_PRIM_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
||||
}
|
||||
|
||||
VkCullModeFlags to_vk_cull_mode_flags(const eGPUFaceCullTest cull_test)
|
||||
{
|
||||
switch (cull_test) {
|
||||
case GPU_CULL_FRONT:
|
||||
return VK_CULL_MODE_FRONT_BIT;
|
||||
case GPU_CULL_BACK:
|
||||
return VK_CULL_MODE_BACK_BIT;
|
||||
case GPU_CULL_NONE:
|
||||
return VK_CULL_MODE_NONE;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return VK_CULL_MODE_NONE;
|
||||
}
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -15,15 +15,20 @@
|
|||
|
||||
#include "vk_mem_alloc.h"
|
||||
|
||||
#include "gpu_index_buffer_private.hh"
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
VkImageAspectFlagBits to_vk_image_aspect_flag_bits(const eGPUTextureFormat format);
|
||||
VkFormat to_vk_format(const eGPUTextureFormat format);
|
||||
VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size);
|
||||
VkComponentMapping to_vk_component_mapping(const eGPUTextureFormat format);
|
||||
VkImageViewType to_vk_image_view_type(const eGPUTextureType type);
|
||||
VkImageType to_vk_image_type(const eGPUTextureType type);
|
||||
VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const void *data);
|
||||
VkIndexType to_vk_index_type(const GPUIndexBufType index_type);
|
||||
VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type);
|
||||
VkCullModeFlags to_vk_cull_mode_flags(const eGPUFaceCullTest cull_test);
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_immediate.hh"
|
||||
#include "vk_memory.hh"
|
||||
#include "vk_shader.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
|
||||
#include "GHOST_C-api.h"
|
||||
|
@ -47,6 +49,7 @@ VKContext::VKContext(void *ghost_window, void *ghost_context)
|
|||
descriptor_pools_.init(vk_device_);
|
||||
|
||||
state_manager = new VKStateManager();
|
||||
imm = new VKImmediate();
|
||||
|
||||
VKBackend::capabilities_init(*this);
|
||||
|
||||
|
@ -56,6 +59,10 @@ VKContext::VKContext(void *ghost_window, void *ghost_context)
|
|||
|
||||
VKContext::~VKContext()
|
||||
{
|
||||
/* IMM owns a buffer that should be freed before the memory allocator is freed. */
|
||||
/* TODO: Introduce a buffer pool for temporary buffers. */
|
||||
delete imm;
|
||||
imm = nullptr;
|
||||
vmaDestroyAllocator(mem_allocator_);
|
||||
}
|
||||
|
||||
|
@ -90,9 +97,14 @@ void VKContext::activate()
|
|||
back_left = framebuffer;
|
||||
framebuffer->bind(false);
|
||||
}
|
||||
|
||||
immActivate();
|
||||
}
|
||||
|
||||
void VKContext::deactivate() {}
|
||||
void VKContext::deactivate()
|
||||
{
|
||||
immDeactivate();
|
||||
}
|
||||
|
||||
void VKContext::begin_frame()
|
||||
{
|
||||
|
@ -106,6 +118,9 @@ void VKContext::begin_frame()
|
|||
|
||||
void VKContext::end_frame()
|
||||
{
|
||||
if (has_active_framebuffer()) {
|
||||
deactivate_framebuffer();
|
||||
}
|
||||
command_buffer_.end_recording();
|
||||
}
|
||||
|
||||
|
@ -116,14 +131,26 @@ void VKContext::flush()
|
|||
|
||||
void VKContext::finish()
|
||||
{
|
||||
if (has_active_framebuffer()) {
|
||||
deactivate_framebuffer();
|
||||
}
|
||||
command_buffer_.submit();
|
||||
}
|
||||
|
||||
void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/) {}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name State manager
|
||||
* \{ */
|
||||
|
||||
const VKStateManager &VKContext::state_manager_get() const
|
||||
{
|
||||
return *static_cast<const VKStateManager *>(state_manager);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Framebuffer
|
||||
* \{ */
|
||||
|
||||
void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer)
|
||||
{
|
||||
if (has_active_framebuffer()) {
|
||||
|
@ -135,17 +162,40 @@ void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer)
|
|||
command_buffer_.begin_render_pass(framebuffer);
|
||||
}
|
||||
|
||||
VKFrameBuffer *VKContext::active_framebuffer_get() const
|
||||
{
|
||||
return unwrap(active_fb);
|
||||
}
|
||||
|
||||
bool VKContext::has_active_framebuffer() const
|
||||
{
|
||||
return active_fb != nullptr;
|
||||
return active_framebuffer_get() != nullptr;
|
||||
}
|
||||
|
||||
void VKContext::deactivate_framebuffer()
|
||||
{
|
||||
BLI_assert(active_fb != nullptr);
|
||||
VKFrameBuffer *framebuffer = unwrap(active_fb);
|
||||
VKFrameBuffer *framebuffer = active_framebuffer_get();
|
||||
BLI_assert(framebuffer != nullptr);
|
||||
command_buffer_.end_render_pass(*framebuffer);
|
||||
active_fb = nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Graphics pipeline
|
||||
* \{ */
|
||||
|
||||
void VKContext::bind_graphics_pipeline(const GPUPrimType prim_type,
|
||||
const VKVertexAttributeObject &vertex_attribute_object)
|
||||
{
|
||||
VKShader *shader = unwrap(this->shader);
|
||||
BLI_assert(shader);
|
||||
shader->update_graphics_pipeline(*this, prim_type, vertex_attribute_object);
|
||||
command_buffer_get().bind(shader->pipeline_get(), VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
shader->pipeline_get().push_constants_get().update(*this);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
namespace blender::gpu {
|
||||
class VKFrameBuffer;
|
||||
class VKVertexAttributeObject;
|
||||
class VKBatch;
|
||||
class VKStateManager;
|
||||
|
||||
class VKContext : public Context {
|
||||
private:
|
||||
|
@ -56,8 +59,13 @@ class VKContext : public Context {
|
|||
bool debug_capture_scope_begin(void *scope) override;
|
||||
void debug_capture_scope_end(void *scope) override;
|
||||
|
||||
bool has_active_framebuffer() const;
|
||||
void activate_framebuffer(VKFrameBuffer &framebuffer);
|
||||
void deactivate_framebuffer();
|
||||
VKFrameBuffer *active_framebuffer_get() const;
|
||||
|
||||
void bind_graphics_pipeline(const GPUPrimType prim_type,
|
||||
const VKVertexAttributeObject &vertex_attribute_object);
|
||||
|
||||
static VKContext *get(void)
|
||||
{
|
||||
|
@ -99,6 +107,8 @@ class VKContext : public Context {
|
|||
return descriptor_pools_;
|
||||
}
|
||||
|
||||
const VKStateManager &state_manager_get() const;
|
||||
|
||||
VmaAllocator mem_allocator_get() const
|
||||
{
|
||||
return mem_allocator_;
|
||||
|
@ -106,8 +116,6 @@ class VKContext : public Context {
|
|||
|
||||
private:
|
||||
void init_physical_device_limits();
|
||||
|
||||
bool has_active_framebuffer() const;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace blender::gpu {
|
|||
VKFrameBuffer::VKFrameBuffer(const char *name) : FrameBuffer(name)
|
||||
{
|
||||
immutable_ = false;
|
||||
size_set(1, 1);
|
||||
}
|
||||
|
||||
VKFrameBuffer::VKFrameBuffer(const char *name,
|
||||
|
@ -29,15 +30,12 @@ VKFrameBuffer::VKFrameBuffer(const char *name,
|
|||
immutable_ = true;
|
||||
/* Never update an internal frame-buffer. */
|
||||
dirty_attachments_ = false;
|
||||
width_ = vk_extent.width;
|
||||
height_ = vk_extent.height;
|
||||
vk_framebuffer_ = vk_framebuffer;
|
||||
vk_render_pass_ = vk_render_pass;
|
||||
|
||||
viewport_[0] = scissor_[0] = 0;
|
||||
viewport_[1] = scissor_[1] = 0;
|
||||
viewport_[2] = scissor_[2] = width_;
|
||||
viewport_[3] = scissor_[3] = height_;
|
||||
size_set(vk_extent.width, vk_extent.height);
|
||||
viewport_reset();
|
||||
scissor_reset();
|
||||
}
|
||||
|
||||
VKFrameBuffer::~VKFrameBuffer()
|
||||
|
@ -51,12 +49,36 @@ VKFrameBuffer::~VKFrameBuffer()
|
|||
|
||||
void VKFrameBuffer::bind(bool /*enabled_srgb*/)
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
/* Updating attachments can issue pipeline barriers, this should be done outside the render pass.
|
||||
* When done inside a render pass there should be a self-dependency between sub-passes on the
|
||||
* active render pass. As the active render pass isn't aware of the new render pass (and should
|
||||
* not) it is better to deactivate it before updating the attachments. For more information check
|
||||
* `VkSubpassDependency`. */
|
||||
if (context.has_active_framebuffer()) {
|
||||
context.deactivate_framebuffer();
|
||||
}
|
||||
|
||||
update_attachments();
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
context.activate_framebuffer(*this);
|
||||
}
|
||||
|
||||
VkViewport VKFrameBuffer::vk_viewport_get() const
|
||||
{
|
||||
VkViewport viewport;
|
||||
int viewport_rect[4];
|
||||
viewport_get(viewport_rect);
|
||||
|
||||
viewport.x = viewport_rect[0];
|
||||
viewport.y = viewport_rect[1];
|
||||
viewport.width = viewport_rect[2];
|
||||
viewport.height = viewport_rect[3];
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
return viewport;
|
||||
}
|
||||
|
||||
VkRect2D VKFrameBuffer::vk_render_area_get() const
|
||||
{
|
||||
VkRect2D render_area = {};
|
||||
|
@ -81,7 +103,7 @@ VkRect2D VKFrameBuffer::vk_render_area_get() const
|
|||
|
||||
bool VKFrameBuffer::check(char /*err_out*/[256])
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKFrameBuffer::build_clear_attachments_depth_stencil(
|
||||
|
@ -188,13 +210,43 @@ void VKFrameBuffer::attachment_set_loadstore_op(GPUAttachmentType /*type*/,
|
|||
/** \name Read back
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::read(eGPUFrameBufferBits /*planes*/,
|
||||
eGPUDataFormat /*format*/,
|
||||
void VKFrameBuffer::read(eGPUFrameBufferBits plane,
|
||||
eGPUDataFormat format,
|
||||
const int /*area*/[4],
|
||||
int /*channel_len*/,
|
||||
int /*slot*/,
|
||||
void * /*r_data*/)
|
||||
int channel_len,
|
||||
int slot,
|
||||
void *r_data)
|
||||
{
|
||||
VKTexture *texture = nullptr;
|
||||
switch (plane) {
|
||||
case GPU_COLOR_BIT:
|
||||
texture = unwrap(unwrap(attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot].tex));
|
||||
break;
|
||||
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert_msg(texture,
|
||||
"Trying to read back color texture from framebuffer, but no color texture is "
|
||||
"available in requested slot.");
|
||||
void *data = texture->read(0, format);
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - Add support for area.
|
||||
* - Add support for channel_len.
|
||||
* Best option would be to add this to a specific interface in VKTexture so we don't
|
||||
* over-allocate and reduce number of times copies are made.
|
||||
*/
|
||||
BLI_assert(format == GPU_DATA_FLOAT);
|
||||
BLI_assert(channel_len == 4);
|
||||
int mip_size[3] = {1, 1, 1};
|
||||
texture->mip_size_get(0, mip_size);
|
||||
const size_t mem_size = mip_size[0] * mip_size[1] * mip_size[2] * sizeof(float) * channel_len;
|
||||
memcpy(r_data, data, mem_size);
|
||||
MEM_freeN(data);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -319,7 +371,8 @@ void VKFrameBuffer::render_pass_create()
|
|||
size_set(size[0], size[1]);
|
||||
}
|
||||
else {
|
||||
this->size_set(0, 0);
|
||||
/* A framebuffer should at least be 1 by 1.*/
|
||||
this->size_set(1, 1);
|
||||
}
|
||||
viewport_reset();
|
||||
scissor_reset();
|
||||
|
|
|
@ -87,6 +87,7 @@ class VKFrameBuffer : public FrameBuffer {
|
|||
BLI_assert(vk_render_pass_ != VK_NULL_HANDLE);
|
||||
return vk_render_pass_;
|
||||
}
|
||||
VkViewport vk_viewport_get() const;
|
||||
VkRect2D vk_render_area_get() const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Mimics old style OpenGL immediate mode drawing.
|
||||
*/
|
||||
|
||||
#include "vk_immediate.hh"
|
||||
|
||||
#include "gpu_vertex_format_private.h"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
VKImmediate::VKImmediate() {}
|
||||
VKImmediate::~VKImmediate() {}
|
||||
|
||||
uchar *VKImmediate::begin()
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len);
|
||||
if (!buffer_.is_allocated()) {
|
||||
buffer_.create(context,
|
||||
DEFAULT_INTERNAL_BUFFER_SIZE,
|
||||
GPU_USAGE_DYNAMIC,
|
||||
static_cast<VkBufferUsageFlagBits>(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT));
|
||||
}
|
||||
|
||||
/* TODO: Resize buffer when more is needed. Currently assert as we haven't implemented it yet. */
|
||||
BLI_assert(bytes_needed < DEFAULT_INTERNAL_BUFFER_SIZE);
|
||||
|
||||
uchar *data = static_cast<uchar *>(buffer_.mapped_memory_get());
|
||||
return data;
|
||||
}
|
||||
|
||||
void VKImmediate::end()
|
||||
{
|
||||
BLI_assert_msg(prim_type != GPU_PRIM_NONE, "Illegal state: not between an immBegin/End pair.");
|
||||
if (vertex_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
BLI_assert(context.shader == unwrap(shader));
|
||||
context.state_manager->apply_state();
|
||||
vertex_attributes_.update_bindings(*this);
|
||||
context.bind_graphics_pipeline(prim_type, vertex_attributes_);
|
||||
vertex_attributes_.bind(context);
|
||||
|
||||
context.command_buffer_get().draw(0, vertex_len, 0, 1);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
|
@ -0,0 +1,41 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Mimics old style OpenGL immediate mode drawing.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "gpu_immediate_private.hh"
|
||||
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_mem_alloc.h"
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* Size of internal buffer. */
|
||||
constexpr size_t DEFAULT_INTERNAL_BUFFER_SIZE = (4 * 1024 * 1024);
|
||||
|
||||
class VKImmediate : public Immediate {
|
||||
private:
|
||||
VKBuffer buffer_;
|
||||
VKVertexAttributeObject vertex_attributes_;
|
||||
|
||||
public:
|
||||
VKImmediate();
|
||||
virtual ~VKImmediate();
|
||||
|
||||
uchar *begin(void) override;
|
||||
void end(void) override;
|
||||
|
||||
friend class VKVertexAttributeObject;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
|
@ -11,15 +11,39 @@
|
|||
|
||||
namespace blender::gpu {
|
||||
|
||||
void VKIndexBuffer::upload_data() {}
|
||||
|
||||
void VKIndexBuffer::bind_as_ssbo(uint binding)
|
||||
void VKIndexBuffer::ensure_updated()
|
||||
{
|
||||
if (is_subrange_) {
|
||||
src_->upload_data();
|
||||
return;
|
||||
}
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
if (!buffer_.is_allocated()) {
|
||||
allocate(context);
|
||||
}
|
||||
|
||||
if (data_ != nullptr) {
|
||||
buffer_.update(data_);
|
||||
MEM_SAFE_FREE(data_);
|
||||
}
|
||||
}
|
||||
|
||||
void VKIndexBuffer::upload_data()
|
||||
{
|
||||
ensure_updated();
|
||||
}
|
||||
|
||||
void VKIndexBuffer::bind(VKContext &context)
|
||||
{
|
||||
context.command_buffer_get().bind(*this, to_vk_index_type(index_type_));
|
||||
}
|
||||
|
||||
void VKIndexBuffer::bind_as_ssbo(uint binding)
|
||||
{
|
||||
ensure_updated();
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
VKShader *shader = static_cast<VKShader *>(context.shader);
|
||||
const VKShaderInterface &shader_interface = shader->interface_get();
|
||||
const VKDescriptorSet::Location location = shader_interface.descriptor_set_location(
|
||||
|
|
|
@ -20,12 +20,13 @@ class VKIndexBuffer : public IndexBuf {
|
|||
void upload_data() override;
|
||||
|
||||
void bind_as_ssbo(uint binding) override;
|
||||
void bind(VKContext &context);
|
||||
|
||||
void read(uint32_t *data) const override;
|
||||
|
||||
void update_sub(uint start, uint len, const void *data) override;
|
||||
|
||||
VkBuffer vk_handle()
|
||||
VkBuffer vk_handle() const
|
||||
{
|
||||
return buffer_.vk_handle();
|
||||
}
|
||||
|
@ -33,6 +34,12 @@ class VKIndexBuffer : public IndexBuf {
|
|||
private:
|
||||
void strip_restart_indices() override;
|
||||
void allocate(VKContext &context);
|
||||
void ensure_updated();
|
||||
};
|
||||
|
||||
static inline VKIndexBuffer *unwrap(IndexBuf *index_buffer)
|
||||
{
|
||||
return static_cast<VKIndexBuffer *>(index_buffer);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -6,11 +6,23 @@
|
|||
*/
|
||||
|
||||
#include "vk_pipeline.hh"
|
||||
#include "vk_batch.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_memory.hh"
|
||||
#include "vk_state_manager.hh"
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
VKPipeline::VKPipeline(VkDescriptorSetLayout vk_descriptor_set_layout,
|
||||
VKPushConstants &&push_constants)
|
||||
: vk_pipeline_(VK_NULL_HANDLE),
|
||||
descriptor_set_(vk_descriptor_set_layout),
|
||||
push_constants_(std::move(push_constants))
|
||||
{
|
||||
}
|
||||
|
||||
VKPipeline::VKPipeline(VkPipeline vk_pipeline,
|
||||
VkDescriptorSetLayout vk_descriptor_set_layout,
|
||||
VKPushConstants &&push_constants)
|
||||
|
@ -60,6 +72,14 @@ VKPipeline VKPipeline::create_compute_pipeline(
|
|||
return VKPipeline(vk_pipeline, descriptor_set_layout, std::move(push_constants));
|
||||
}
|
||||
|
||||
VKPipeline VKPipeline::create_graphics_pipeline(
|
||||
VkDescriptorSetLayout &descriptor_set_layout,
|
||||
const VKPushConstants::Layout &push_constants_layout)
|
||||
{
|
||||
VKPushConstants push_constants(&push_constants_layout);
|
||||
return VKPipeline(descriptor_set_layout, std::move(push_constants));
|
||||
}
|
||||
|
||||
VkPipeline VKPipeline::vk_handle() const
|
||||
{
|
||||
return vk_pipeline_;
|
||||
|
@ -70,4 +90,87 @@ bool VKPipeline::is_valid() const
|
|||
return vk_pipeline_ != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void VKPipeline::finalize(VKContext &context,
|
||||
VkShaderModule vertex_module,
|
||||
VkShaderModule fragment_module,
|
||||
VkPipelineLayout &pipeline_layout,
|
||||
const GPUPrimType prim_type,
|
||||
const VKVertexAttributeObject &vertex_attribute_object)
|
||||
{
|
||||
BLI_assert(vertex_module != VK_NULL_HANDLE);
|
||||
|
||||
VK_ALLOCATION_CALLBACKS
|
||||
|
||||
Vector<VkPipelineShaderStageCreateInfo> pipeline_stages;
|
||||
VkPipelineShaderStageCreateInfo vertex_stage_info = {};
|
||||
vertex_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
vertex_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vertex_stage_info.module = vertex_module;
|
||||
vertex_stage_info.pName = "main";
|
||||
pipeline_stages.append(vertex_stage_info);
|
||||
|
||||
if (fragment_module != VK_NULL_HANDLE) {
|
||||
VkPipelineShaderStageCreateInfo fragment_stage_info = {};
|
||||
fragment_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
fragment_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fragment_stage_info.module = fragment_module;
|
||||
fragment_stage_info.pName = "main";
|
||||
pipeline_stages.append(fragment_stage_info);
|
||||
}
|
||||
|
||||
VKFrameBuffer &framebuffer = *context.active_framebuffer_get();
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_create_info = {};
|
||||
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipeline_create_info.stageCount = pipeline_stages.size();
|
||||
pipeline_create_info.pStages = pipeline_stages.data();
|
||||
pipeline_create_info.layout = pipeline_layout;
|
||||
pipeline_create_info.renderPass = framebuffer.vk_render_pass_get();
|
||||
pipeline_create_info.subpass = 0;
|
||||
|
||||
/* Vertex input state. */
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_state = {};
|
||||
vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertex_input_state.vertexBindingDescriptionCount = 0;
|
||||
vertex_input_state.vertexBindingDescriptionCount = vertex_attribute_object.bindings.size();
|
||||
vertex_input_state.pVertexBindingDescriptions = vertex_attribute_object.bindings.data();
|
||||
vertex_input_state.vertexAttributeDescriptionCount = vertex_attribute_object.attributes.size();
|
||||
vertex_input_state.pVertexAttributeDescriptions = vertex_attribute_object.attributes.data();
|
||||
pipeline_create_info.pVertexInputState = &vertex_input_state;
|
||||
|
||||
/* Input assembly state. */
|
||||
VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly = {};
|
||||
pipeline_input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
pipeline_input_assembly.topology = to_vk_primitive_topology(prim_type);
|
||||
pipeline_create_info.pInputAssemblyState = &pipeline_input_assembly;
|
||||
|
||||
/* Viewport state. */
|
||||
VkPipelineViewportStateCreateInfo viewport_state = {};
|
||||
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
VkViewport viewport = framebuffer.vk_viewport_get();
|
||||
viewport_state.pViewports = &viewport;
|
||||
viewport_state.viewportCount = 1;
|
||||
VkRect2D scissor = framebuffer.vk_render_area_get();
|
||||
viewport_state.pScissors = &scissor;
|
||||
viewport_state.scissorCount = 1;
|
||||
pipeline_create_info.pViewportState = &viewport_state;
|
||||
|
||||
/* Multisample state. */
|
||||
VkPipelineMultisampleStateCreateInfo multisample_state = {};
|
||||
multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
multisample_state.minSampleShading = 1.0f;
|
||||
pipeline_create_info.pMultisampleState = &multisample_state;
|
||||
|
||||
/* States from the state manager. */
|
||||
const VKPipelineStateManager &state_manager = state_manager_get();
|
||||
pipeline_create_info.pColorBlendState = &state_manager.pipeline_color_blend_state;
|
||||
pipeline_create_info.pRasterizationState = &state_manager.rasterization_state;
|
||||
pipeline_create_info.pDepthStencilState = &state_manager.depth_stencil_state;
|
||||
|
||||
VkDevice vk_device = context.device_get();
|
||||
vkCreateGraphicsPipelines(
|
||||
vk_device, VK_NULL_HANDLE, 1, &pipeline_create_info, vk_allocation_callbacks, &vk_pipeline_);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -14,20 +14,35 @@
|
|||
|
||||
#include "vk_common.hh"
|
||||
#include "vk_descriptor_set.hh"
|
||||
#include "vk_pipeline_state.hh"
|
||||
#include "vk_push_constants.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKContext;
|
||||
class VKShader;
|
||||
class VKVertexAttributeObject;
|
||||
class VKBatch;
|
||||
|
||||
/**
|
||||
* Pipeline can be a compute pipeline or a graphic pipeline.
|
||||
*
|
||||
* Compute pipelines can be constructed early on, but graphics
|
||||
* pipelines depends on the actual GPU state/context.
|
||||
*
|
||||
* - TODO: we should sanitize the interface. There we can also
|
||||
* use late construction for compute pipelines.
|
||||
*/
|
||||
class VKPipeline : NonCopyable {
|
||||
VkPipeline vk_pipeline_ = VK_NULL_HANDLE;
|
||||
VKDescriptorSetTracker descriptor_set_;
|
||||
VKPushConstants push_constants_;
|
||||
VKPipelineStateManager state_manager_;
|
||||
|
||||
public:
|
||||
VKPipeline() = default;
|
||||
|
||||
virtual ~VKPipeline();
|
||||
VKPipeline(VkDescriptorSetLayout vk_descriptor_set_layout, VKPushConstants &&push_constants);
|
||||
VKPipeline(VkPipeline vk_pipeline,
|
||||
VkDescriptorSetLayout vk_descriptor_set_layout,
|
||||
VKPushConstants &&push_constants);
|
||||
|
@ -45,6 +60,8 @@ class VKPipeline : NonCopyable {
|
|||
VkDescriptorSetLayout &descriptor_set_layout,
|
||||
VkPipelineLayout &pipeline_layouts,
|
||||
const VKPushConstants::Layout &push_constants_layout);
|
||||
static VKPipeline create_graphics_pipeline(VkDescriptorSetLayout &descriptor_set_layout,
|
||||
const VKPushConstants::Layout &push_constants_layout);
|
||||
|
||||
VKDescriptorSetTracker &descriptor_set_get()
|
||||
{
|
||||
|
@ -56,8 +73,20 @@ class VKPipeline : NonCopyable {
|
|||
return push_constants_;
|
||||
}
|
||||
|
||||
VKPipelineStateManager &state_manager_get()
|
||||
{
|
||||
return state_manager_;
|
||||
}
|
||||
|
||||
VkPipeline vk_handle() const;
|
||||
bool is_valid() const;
|
||||
|
||||
void finalize(VKContext &context,
|
||||
VkShaderModule vertex_module,
|
||||
VkShaderModule fragment_module,
|
||||
VkPipelineLayout &pipeline_layout,
|
||||
const GPUPrimType prim_type,
|
||||
const VKVertexAttributeObject &vertex_attribute_object);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_pipeline_state.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
VKPipelineStateManager::VKPipelineStateManager()
|
||||
{
|
||||
rasterization_state = {};
|
||||
rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rasterization_state.lineWidth = 1.0f;
|
||||
|
||||
pipeline_color_blend_state = {};
|
||||
pipeline_color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
|
||||
depth_stencil_state = {};
|
||||
depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_state(const GPUState &state, const GPUStateMutable &mutable_state)
|
||||
{
|
||||
/* TODO should be extracted from current framebuffer and should not be done here and now. */
|
||||
color_blend_attachments.clear();
|
||||
VkPipelineColorBlendAttachmentState color_blend_attachment = {};
|
||||
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
color_blend_attachments.append(color_blend_attachment);
|
||||
pipeline_color_blend_state.attachmentCount = color_blend_attachments.size();
|
||||
pipeline_color_blend_state.pAttachments = color_blend_attachments.data();
|
||||
|
||||
GPUState changed = state ^ current_;
|
||||
if (changed.blend) {
|
||||
set_blend(static_cast<eGPUBlend>(state.blend));
|
||||
}
|
||||
if (changed.write_mask != 0) {
|
||||
set_write_mask((eGPUWriteMask)state.write_mask);
|
||||
}
|
||||
if (changed.depth_test != 0) {
|
||||
set_depth_test((eGPUDepthTest)state.depth_test);
|
||||
}
|
||||
if (changed.stencil_test != 0 || changed.stencil_op != 0) {
|
||||
set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op);
|
||||
set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state);
|
||||
}
|
||||
if (changed.clip_distances != 0) {
|
||||
set_clip_distances(state.clip_distances, current_.clip_distances);
|
||||
}
|
||||
if (changed.culling_test != 0) {
|
||||
set_backface_culling((eGPUFaceCullTest)state.culling_test);
|
||||
}
|
||||
if (changed.logic_op_xor != 0) {
|
||||
set_logic_op(state.logic_op_xor);
|
||||
}
|
||||
if (changed.invert_facing != 0) {
|
||||
set_facing(state.invert_facing);
|
||||
}
|
||||
if (changed.provoking_vert != 0) {
|
||||
set_provoking_vert((eGPUProvokingVertex)state.provoking_vert);
|
||||
}
|
||||
if (changed.shadow_bias != 0) {
|
||||
set_shadow_bias(state.shadow_bias);
|
||||
}
|
||||
current_ = state;
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::force_state(const GPUState &state,
|
||||
const GPUStateMutable &mutable_state)
|
||||
{
|
||||
current_ = ~state;
|
||||
set_state(state, mutable_state);
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_blend(const eGPUBlend blend)
|
||||
{
|
||||
VkPipelineColorBlendStateCreateInfo &cb = pipeline_color_blend_state;
|
||||
VkPipelineColorBlendAttachmentState &att_state = color_blend_attachments.last();
|
||||
|
||||
att_state.blendEnable = VK_TRUE;
|
||||
att_state.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
att_state.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
cb.blendConstants[0] = 1.0f;
|
||||
cb.blendConstants[1] = 1.0f;
|
||||
cb.blendConstants[2] = 1.0f;
|
||||
cb.blendConstants[3] = 1.0f;
|
||||
|
||||
switch (blend) {
|
||||
default:
|
||||
case GPU_BLEND_ALPHA:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_ALPHA_PREMULT:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_ADDITIVE:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_SUBTRACT:
|
||||
case GPU_BLEND_ADDITIVE_PREMULT:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_MULTIPLY:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_DST_COLOR;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_INVERT:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_OIT:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_BACKGROUND:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_ALPHA_UNDER_PREMUL:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
break;
|
||||
|
||||
case GPU_BLEND_CUSTOM:
|
||||
att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC1_COLOR;
|
||||
att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_SRC1_ALPHA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (blend == GPU_BLEND_SUBTRACT) {
|
||||
att_state.alphaBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
|
||||
att_state.colorBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
|
||||
}
|
||||
else {
|
||||
att_state.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
att_state.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
}
|
||||
|
||||
if (blend != GPU_BLEND_NONE) {
|
||||
att_state.blendEnable = VK_TRUE;
|
||||
}
|
||||
else {
|
||||
att_state.blendEnable = VK_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_write_mask(const eGPUWriteMask write_mask)
|
||||
{
|
||||
depth_stencil_state.depthWriteEnable = (write_mask & GPU_WRITE_DEPTH) ? VK_TRUE : VK_FALSE;
|
||||
|
||||
VkPipelineColorBlendAttachmentState &att_state = color_blend_attachments.last();
|
||||
att_state.colorWriteMask = 0;
|
||||
|
||||
if ((write_mask & GPU_WRITE_RED) != 0) {
|
||||
att_state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT;
|
||||
}
|
||||
if ((write_mask & GPU_WRITE_GREEN) != 0) {
|
||||
att_state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT;
|
||||
}
|
||||
if ((write_mask & GPU_WRITE_BLUE) != 0) {
|
||||
att_state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT;
|
||||
}
|
||||
if ((write_mask & GPU_WRITE_ALPHA) != 0) {
|
||||
att_state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_depth_test(const eGPUDepthTest value)
|
||||
{
|
||||
switch (value) {
|
||||
case GPU_DEPTH_LESS:
|
||||
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_LESS;
|
||||
break;
|
||||
case GPU_DEPTH_LESS_EQUAL:
|
||||
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
break;
|
||||
case GPU_DEPTH_EQUAL:
|
||||
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_EQUAL;
|
||||
break;
|
||||
case GPU_DEPTH_GREATER:
|
||||
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_GREATER;
|
||||
break;
|
||||
case GPU_DEPTH_GREATER_EQUAL:
|
||||
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
|
||||
break;
|
||||
case GPU_DEPTH_ALWAYS:
|
||||
default:
|
||||
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_ALWAYS;
|
||||
break;
|
||||
}
|
||||
|
||||
if (value != GPU_DEPTH_NONE) {
|
||||
depth_stencil_state.depthTestEnable = VK_TRUE;
|
||||
}
|
||||
else {
|
||||
depth_stencil_state.depthTestEnable = VK_FALSE;
|
||||
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_NEVER;
|
||||
}
|
||||
|
||||
depth_stencil_state.depthBoundsTestEnable = VK_TRUE;
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_stencil_test(const eGPUStencilTest test,
|
||||
const eGPUStencilOp operation)
|
||||
{
|
||||
depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
depth_stencil_state.front.compareMask = 0;
|
||||
depth_stencil_state.front.reference = 0;
|
||||
|
||||
switch (operation) {
|
||||
case GPU_STENCIL_OP_REPLACE:
|
||||
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_REPLACE;
|
||||
depth_stencil_state.back = depth_stencil_state.front;
|
||||
break;
|
||||
|
||||
case GPU_STENCIL_OP_COUNT_DEPTH_PASS:
|
||||
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_DECREMENT_AND_WRAP;
|
||||
depth_stencil_state.back = depth_stencil_state.front;
|
||||
depth_stencil_state.back.depthFailOp = VK_STENCIL_OP_INCREMENT_AND_WRAP;
|
||||
break;
|
||||
|
||||
case GPU_STENCIL_OP_COUNT_DEPTH_FAIL:
|
||||
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.front.passOp = VK_STENCIL_OP_INCREMENT_AND_WRAP;
|
||||
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.back = depth_stencil_state.front;
|
||||
depth_stencil_state.back.depthFailOp = VK_STENCIL_OP_DECREMENT_AND_WRAP;
|
||||
break;
|
||||
|
||||
case GPU_STENCIL_OP_NONE:
|
||||
default:
|
||||
depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP;
|
||||
depth_stencil_state.back = depth_stencil_state.front;
|
||||
break;
|
||||
}
|
||||
|
||||
if (test != GPU_STENCIL_NONE) {
|
||||
depth_stencil_state.stencilTestEnable = VK_TRUE;
|
||||
}
|
||||
else {
|
||||
depth_stencil_state.stencilTestEnable = VK_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_stencil_mask(const eGPUStencilTest test,
|
||||
const GPUStateMutable &mutable_state)
|
||||
{
|
||||
depth_stencil_state.front.writeMask = static_cast<uint32_t>(mutable_state.stencil_write_mask);
|
||||
depth_stencil_state.front.reference = static_cast<uint32_t>(mutable_state.stencil_reference);
|
||||
|
||||
depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
depth_stencil_state.front.compareMask = static_cast<uint32_t>(
|
||||
mutable_state.stencil_compare_mask);
|
||||
|
||||
switch (test) {
|
||||
case GPU_STENCIL_NEQUAL:
|
||||
depth_stencil_state.front.compareOp = VK_COMPARE_OP_NOT_EQUAL;
|
||||
break;
|
||||
case GPU_STENCIL_EQUAL:
|
||||
depth_stencil_state.front.compareOp = VK_COMPARE_OP_EQUAL;
|
||||
break;
|
||||
case GPU_STENCIL_ALWAYS:
|
||||
depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
break;
|
||||
case GPU_STENCIL_NONE:
|
||||
default:
|
||||
depth_stencil_state.front.compareMask = 0x00;
|
||||
depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
return;
|
||||
}
|
||||
|
||||
depth_stencil_state.back = depth_stencil_state.front;
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_clip_distances(const int /*new_dist_len*/,
|
||||
const int /*old_dist_len*/)
|
||||
{
|
||||
/* TODO: needs to be implemented. */
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_logic_op(const bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
pipeline_color_blend_state.logicOpEnable = VK_TRUE;
|
||||
pipeline_color_blend_state.logicOp = VK_LOGIC_OP_XOR;
|
||||
}
|
||||
else {
|
||||
pipeline_color_blend_state.logicOpEnable = VK_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_facing(const bool invert)
|
||||
{
|
||||
rasterization_state.frontFace = invert ? VK_FRONT_FACE_COUNTER_CLOCKWISE :
|
||||
VK_FRONT_FACE_CLOCKWISE;
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_backface_culling(const eGPUFaceCullTest cull_test)
|
||||
{
|
||||
rasterization_state.cullMode = to_vk_cull_mode_flags(cull_test);
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_provoking_vert(const eGPUProvokingVertex /*vert*/)
|
||||
{
|
||||
/* TODO: Requires VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, See:
|
||||
* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineRasterizationProvokingVertexStateCreateInfoEXT.html
|
||||
*/
|
||||
}
|
||||
|
||||
void VKPipelineStateManager::set_shadow_bias(const bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
rasterization_state.depthBiasEnable = VK_TRUE;
|
||||
rasterization_state.depthBiasSlopeFactor = 2.f;
|
||||
rasterization_state.depthBiasConstantFactor = 1.f;
|
||||
rasterization_state.depthBiasClamp = 0.f;
|
||||
}
|
||||
else {
|
||||
rasterization_state.depthBiasEnable = VK_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
|
@ -0,0 +1,46 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "gpu_state_private.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class VKPipelineStateManager {
|
||||
private:
|
||||
GPUState current_;
|
||||
GPUStateMutable current_mutable_;
|
||||
|
||||
public:
|
||||
VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state;
|
||||
Vector<VkPipelineColorBlendAttachmentState> color_blend_attachments;
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_state;
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil_state;
|
||||
|
||||
VKPipelineStateManager();
|
||||
|
||||
void set_state(const GPUState &state, const GPUStateMutable &mutable_state);
|
||||
void force_state(const GPUState &state, const GPUStateMutable &mutable_state);
|
||||
|
||||
private:
|
||||
void set_blend(eGPUBlend blend);
|
||||
void set_write_mask(eGPUWriteMask write_mask);
|
||||
void set_depth_test(eGPUDepthTest value);
|
||||
void set_stencil_test(eGPUStencilTest test, eGPUStencilOp operation);
|
||||
void set_stencil_mask(eGPUStencilTest test, const GPUStateMutable &mutable_state);
|
||||
void set_clip_distances(int new_dist_len, int old_dist_len);
|
||||
void set_logic_op(bool enable);
|
||||
void set_facing(bool invert);
|
||||
void set_backface_culling(eGPUFaceCullTest test);
|
||||
void set_provoking_vert(eGPUProvokingVertex vert);
|
||||
void set_shadow_bias(bool enable);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
|
@ -668,20 +668,21 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
|
|||
BLI_assert((fragment_module_ != VK_NULL_HANDLE && info->tf_type_ == GPU_SHADER_TFB_NONE) ||
|
||||
(fragment_module_ == VK_NULL_HANDLE && info->tf_type_ != GPU_SHADER_TFB_NONE));
|
||||
BLI_assert(compute_module_ == VK_NULL_HANDLE);
|
||||
result = finalize_graphics_pipeline(vk_device);
|
||||
pipeline_ = VKPipeline::create_graphics_pipeline(layout_,
|
||||
vk_interface->push_constants_layout_get());
|
||||
result = true;
|
||||
}
|
||||
else {
|
||||
BLI_assert(vertex_module_ == VK_NULL_HANDLE);
|
||||
BLI_assert(geometry_module_ == VK_NULL_HANDLE);
|
||||
BLI_assert(fragment_module_ == VK_NULL_HANDLE);
|
||||
BLI_assert(compute_module_ != VK_NULL_HANDLE);
|
||||
compute_pipeline_ = VKPipeline::create_compute_pipeline(
|
||||
*context_,
|
||||
compute_module_,
|
||||
layout_,
|
||||
pipeline_layout_,
|
||||
vk_interface->push_constants_layout_get());
|
||||
result = compute_pipeline_.is_valid();
|
||||
pipeline_ = VKPipeline::create_compute_pipeline(*context_,
|
||||
compute_module_,
|
||||
layout_,
|
||||
pipeline_layout_,
|
||||
vk_interface->push_constants_layout_get());
|
||||
result = pipeline_.is_valid();
|
||||
}
|
||||
|
||||
if (result) {
|
||||
|
@ -693,36 +694,6 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool VKShader::finalize_graphics_pipeline(VkDevice /*vk_device */)
|
||||
{
|
||||
Vector<VkPipelineShaderStageCreateInfo> pipeline_stages;
|
||||
VkPipelineShaderStageCreateInfo vertex_stage_info = {};
|
||||
vertex_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
vertex_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vertex_stage_info.module = vertex_module_;
|
||||
vertex_stage_info.pName = "main";
|
||||
pipeline_stages.append(vertex_stage_info);
|
||||
|
||||
if (geometry_module_ != VK_NULL_HANDLE) {
|
||||
VkPipelineShaderStageCreateInfo geo_stage_info = {};
|
||||
geo_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
geo_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||
geo_stage_info.module = geometry_module_;
|
||||
geo_stage_info.pName = "main";
|
||||
pipeline_stages.append(geo_stage_info);
|
||||
}
|
||||
if (fragment_module_ != VK_NULL_HANDLE) {
|
||||
VkPipelineShaderStageCreateInfo fragment_stage_info = {};
|
||||
fragment_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
fragment_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fragment_stage_info.module = fragment_module_;
|
||||
fragment_stage_info.pName = "main";
|
||||
pipeline_stages.append(fragment_stage_info);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VKShader::finalize_pipeline_layout(VkDevice vk_device,
|
||||
const VKShaderInterface &shader_interface)
|
||||
{
|
||||
|
@ -961,26 +932,32 @@ bool VKShader::transform_feedback_enable(GPUVertBuf *)
|
|||
|
||||
void VKShader::transform_feedback_disable() {}
|
||||
|
||||
void VKShader::update_graphics_pipeline(VKContext &context,
|
||||
const GPUPrimType prim_type,
|
||||
const VKVertexAttributeObject &vertex_attribute_object)
|
||||
{
|
||||
BLI_assert(is_graphics_shader());
|
||||
pipeline_get().finalize(context,
|
||||
vertex_module_,
|
||||
fragment_module_,
|
||||
pipeline_layout_,
|
||||
prim_type,
|
||||
vertex_attribute_object);
|
||||
}
|
||||
|
||||
void VKShader::bind()
|
||||
{
|
||||
VKContext *context = VKContext::get();
|
||||
|
||||
if (is_compute_shader()) {
|
||||
context->command_buffer_get().bind(compute_pipeline_, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
context->command_buffer_get().bind(pipeline_, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
}
|
||||
|
||||
/* Graphics pipeline needs to be constructed just before drawing in order to take the GPU state
|
||||
* into account. GPU state can be changed after binding the shader. */
|
||||
}
|
||||
|
||||
void VKShader::unbind()
|
||||
{
|
||||
if (is_compute_shader()) {
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
void VKShader::unbind() {}
|
||||
|
||||
void VKShader::uniform_float(int location, int comp_len, int array_size, const float *data)
|
||||
{
|
||||
|
@ -1217,7 +1194,7 @@ int VKShader::program_handle_get() const
|
|||
|
||||
VKPipeline &VKShader::pipeline_get()
|
||||
{
|
||||
return compute_pipeline_;
|
||||
return pipeline_;
|
||||
}
|
||||
|
||||
const VKShaderInterface &VKShader::interface_get() const
|
||||
|
|
|
@ -28,7 +28,7 @@ class VKShader : public Shader {
|
|||
bool compilation_failed_ = false;
|
||||
VkDescriptorSetLayout layout_ = VK_NULL_HANDLE;
|
||||
VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
|
||||
VKPipeline compute_pipeline_;
|
||||
VKPipeline pipeline_;
|
||||
|
||||
public:
|
||||
VKShader(const char *name);
|
||||
|
@ -70,6 +70,10 @@ class VKShader : public Shader {
|
|||
|
||||
const VKShaderInterface &interface_get() const;
|
||||
|
||||
void update_graphics_pipeline(VKContext &context,
|
||||
const GPUPrimType prim_type,
|
||||
const VKVertexAttributeObject &vertex_attribute_object);
|
||||
|
||||
private:
|
||||
Vector<uint32_t> compile_glsl_to_spirv(Span<const char *> sources, shaderc_shader_kind kind);
|
||||
void build_shader_module(Span<uint32_t> spirv_module, VkShaderModule *r_shader_module);
|
||||
|
@ -80,7 +84,6 @@ class VKShader : public Shader {
|
|||
const VKShaderInterface &shader_interface,
|
||||
const shader::ShaderCreateInfo &info);
|
||||
bool finalize_pipeline_layout(VkDevice vk_device, const VKShaderInterface &shader_interface);
|
||||
bool finalize_graphics_pipeline(VkDevice vk_device);
|
||||
|
||||
bool is_graphics_shader() const
|
||||
{
|
||||
|
@ -93,4 +96,14 @@ class VKShader : public Shader {
|
|||
}
|
||||
};
|
||||
|
||||
static inline VKShader &unwrap(Shader &shader)
|
||||
{
|
||||
return static_cast<VKShader &>(shader);
|
||||
}
|
||||
|
||||
static inline VKShader *unwrap(Shader *shader)
|
||||
{
|
||||
return static_cast<VKShader *>(shader);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -17,7 +17,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
|
|||
|
||||
using namespace blender::gpu::shader;
|
||||
|
||||
attr_len_ = 0;
|
||||
attr_len_ = info.vertex_inputs_.size();
|
||||
uniform_len_ = info.push_constants_.size();
|
||||
ssbo_len_ = 0;
|
||||
ubo_len_ = 0;
|
||||
|
@ -58,7 +58,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
|
|||
/* Make sure that the image slots don't overlap with the sampler slots. */
|
||||
image_offset_++;
|
||||
|
||||
int32_t input_tot_len = ubo_len_ + uniform_len_ + ssbo_len_;
|
||||
int32_t input_tot_len = attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_;
|
||||
inputs_ = static_cast<ShaderInput *>(
|
||||
MEM_calloc_arrayN(input_tot_len, sizeof(ShaderInput), __func__));
|
||||
ShaderInput *input = inputs_;
|
||||
|
@ -66,6 +66,20 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
|
|||
name_buffer_ = (char *)MEM_mallocN(names_size, "name_buffer");
|
||||
uint32_t name_buffer_offset = 0;
|
||||
|
||||
/* Attributes */
|
||||
for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
|
||||
copy_input_name(input, attr.name, name_buffer_, name_buffer_offset);
|
||||
input->location = input->binding = attr.index;
|
||||
if (input->location != -1) {
|
||||
enabled_attr_mask_ |= (1 << input->location);
|
||||
|
||||
/* Used in `GPU_shader_get_attribute_info`. */
|
||||
attr_types_[input->location] = uint8_t(attr.type);
|
||||
}
|
||||
|
||||
input++;
|
||||
}
|
||||
|
||||
/* Uniform blocks */
|
||||
for (const ShaderCreateInfo::Resource &res : all_resources) {
|
||||
if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
|
||||
|
|
|
@ -6,12 +6,28 @@
|
|||
*/
|
||||
|
||||
#include "vk_state_manager.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_pipeline.hh"
|
||||
#include "vk_shader.hh"
|
||||
#include "vk_texture.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
void VKStateManager::apply_state() {}
|
||||
|
||||
void VKStateManager::force_state() {}
|
||||
void VKStateManager::apply_state()
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
VKShader &shader = unwrap(*context.shader);
|
||||
VKPipeline &pipeline = shader.pipeline_get();
|
||||
pipeline.state_manager_get().set_state(state, mutable_state);
|
||||
}
|
||||
|
||||
void VKStateManager::force_state()
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
VKShader &shader = unwrap(*context.shader);
|
||||
VKPipeline &pipeline = shader.pipeline_get();
|
||||
pipeline.state_manager_get().force_state(state, mutable_state);
|
||||
}
|
||||
|
||||
void VKStateManager::issue_barrier(eGPUBarrier /*barrier_bits*/)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. All rights reserved. */
|
||||
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
|
||||
#include "vk_batch.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_immediate.hh"
|
||||
#include "vk_shader.hh"
|
||||
#include "vk_shader_interface.hh"
|
||||
#include "vk_vertex_buffer.hh"
|
||||
|
||||
#include "BLI_array.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
VKVertexAttributeObject::VKVertexAttributeObject()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void VKVertexAttributeObject::clear()
|
||||
{
|
||||
is_valid = false;
|
||||
info.pNext = NULL;
|
||||
bindings.clear();
|
||||
attributes.clear();
|
||||
vbos.clear();
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
VKVertexAttributeObject &VKVertexAttributeObject::operator=(const VKVertexAttributeObject &other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
is_valid = other.is_valid;
|
||||
info = other.info;
|
||||
bindings.clear();
|
||||
bindings.extend(other.bindings);
|
||||
attributes.clear();
|
||||
attributes.extend(other.attributes);
|
||||
vbos.clear();
|
||||
vbos.extend(other.vbos);
|
||||
buffers.clear();
|
||||
buffers.extend(other.buffers);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void VKVertexAttributeObject::bind(VKContext &context)
|
||||
{
|
||||
Array<bool> visited_bindings(bindings.size());
|
||||
visited_bindings.fill(false);
|
||||
|
||||
for (VkVertexInputAttributeDescription attribute : attributes) {
|
||||
if (visited_bindings[attribute.binding]) {
|
||||
continue;
|
||||
}
|
||||
visited_bindings[attribute.binding] = true;
|
||||
|
||||
/* Bind VBOS from batches. */
|
||||
if (attribute.binding < vbos.size()) {
|
||||
BLI_assert(vbos[attribute.binding]);
|
||||
VKVertexBuffer &vbo = *vbos[attribute.binding];
|
||||
vbo.upload();
|
||||
context.command_buffer_get().bind(attribute.binding, vbo, 0);
|
||||
}
|
||||
|
||||
/* Bind dynamic buffers from immediate mode. */
|
||||
if (attribute.binding < buffers.size()) {
|
||||
BLI_assert(buffers[attribute.binding]);
|
||||
VKBuffer &buffer = *buffers[attribute.binding];
|
||||
context.command_buffer_get().bind(attribute.binding, buffer.vk_handle(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch &batch)
|
||||
{
|
||||
clear();
|
||||
const VKShaderInterface &interface = unwrap(context.shader)->interface_get();
|
||||
AttributeMask occupied_attributes = 0;
|
||||
|
||||
for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
|
||||
VKVertexBuffer *vbo = batch.vertex_buffer_get(v);
|
||||
if (vbo) {
|
||||
update_bindings(
|
||||
vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (int v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) {
|
||||
VKVertexBuffer *vbo = batch.instance_buffer_get(v);
|
||||
if (vbo) {
|
||||
update_bindings(
|
||||
vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, false);
|
||||
}
|
||||
}
|
||||
|
||||
is_valid = true;
|
||||
|
||||
BLI_assert(interface.enabled_attr_mask_ == occupied_attributes);
|
||||
}
|
||||
|
||||
void VKVertexAttributeObject::update_bindings(VKImmediate &immediate)
|
||||
{
|
||||
clear();
|
||||
const VKShaderInterface &interface = unwrap(unwrap(immediate.shader))->interface_get();
|
||||
AttributeMask occupied_attributes = 0;
|
||||
|
||||
update_bindings(immediate.vertex_format,
|
||||
nullptr,
|
||||
&immediate.buffer_,
|
||||
immediate.vertex_len,
|
||||
interface,
|
||||
occupied_attributes,
|
||||
false);
|
||||
is_valid = true;
|
||||
BLI_assert(interface.enabled_attr_mask_ == occupied_attributes);
|
||||
}
|
||||
|
||||
void VKVertexAttributeObject::update_bindings(const GPUVertFormat &vertex_format,
|
||||
VKVertexBuffer *vertex_buffer,
|
||||
VKBuffer *immediate_vertex_buffer,
|
||||
const int64_t vertex_len,
|
||||
const VKShaderInterface &interface,
|
||||
AttributeMask &r_occupied_attributes,
|
||||
const bool use_instancing)
|
||||
{
|
||||
BLI_assert(vertex_buffer || immediate_vertex_buffer);
|
||||
BLI_assert(!(vertex_buffer && immediate_vertex_buffer));
|
||||
|
||||
if (vertex_format.attr_len <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t offset = 0;
|
||||
uint32_t stride = vertex_format.stride;
|
||||
|
||||
for (uint32_t attribute_index = 0; attribute_index < vertex_format.attr_len; attribute_index++) {
|
||||
const GPUVertAttr &attribute = vertex_format.attrs[attribute_index];
|
||||
if (vertex_format.deinterleaved) {
|
||||
offset += ((attribute_index == 0) ? 0 : vertex_format.attrs[attribute_index - 1].size) *
|
||||
vertex_len;
|
||||
stride = attribute.size;
|
||||
}
|
||||
else {
|
||||
offset = attribute.offset;
|
||||
}
|
||||
|
||||
const uint32_t binding = bindings.size();
|
||||
|
||||
bool attribute_used_by_shader = false;
|
||||
for (uint32_t name_index = 0; name_index < attribute.name_len; name_index++) {
|
||||
const char *name = GPU_vertformat_attr_name_get(&vertex_format, &attribute, name_index);
|
||||
const ShaderInput *shader_input = interface.attr_get(name);
|
||||
if (shader_input == nullptr || shader_input->location == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Don't overwrite attributes that are already occupied. */
|
||||
AttributeMask attribute_mask = 1 << shader_input->location;
|
||||
if (r_occupied_attributes & attribute_mask) {
|
||||
continue;
|
||||
}
|
||||
r_occupied_attributes |= attribute_mask;
|
||||
attribute_used_by_shader = true;
|
||||
|
||||
VkVertexInputAttributeDescription attribute_description = {};
|
||||
attribute_description.binding = binding;
|
||||
attribute_description.location = shader_input->location;
|
||||
attribute_description.offset = offset;
|
||||
attribute_description.format = to_vk_format(
|
||||
static_cast<GPUVertCompType>(attribute.comp_type), attribute.size);
|
||||
attributes.append(attribute_description);
|
||||
}
|
||||
|
||||
if (attribute_used_by_shader) {
|
||||
VkVertexInputBindingDescription vk_binding_descriptor = {};
|
||||
vk_binding_descriptor.binding = binding;
|
||||
vk_binding_descriptor.stride = stride;
|
||||
vk_binding_descriptor.inputRate = use_instancing ? VK_VERTEX_INPUT_RATE_INSTANCE :
|
||||
VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
bindings.append(vk_binding_descriptor);
|
||||
if (vertex_buffer) {
|
||||
vbos.append(vertex_buffer);
|
||||
}
|
||||
if (immediate_vertex_buffer) {
|
||||
buffers.append(immediate_vertex_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
|
@ -0,0 +1,58 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. All rights reserved. */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class VKVertexBuffer;
|
||||
class VKContext;
|
||||
class VKBatch;
|
||||
class VKBuffer;
|
||||
class VKShaderInterface;
|
||||
class VKImmediate;
|
||||
|
||||
using AttributeMask = uint16_t;
|
||||
|
||||
struct VKVertexAttributeObject {
|
||||
bool is_valid = false;
|
||||
VkPipelineVertexInputStateCreateInfo info = {
|
||||
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, NULL};
|
||||
|
||||
Vector<VkVertexInputBindingDescription> bindings;
|
||||
Vector<VkVertexInputAttributeDescription> attributes;
|
||||
/* Used for batches. */
|
||||
Vector<VKVertexBuffer *> vbos;
|
||||
/* Used for immediate mode. */
|
||||
Vector<VKBuffer *> buffers;
|
||||
|
||||
VKVertexAttributeObject();
|
||||
void clear();
|
||||
|
||||
void bind(VKContext &context);
|
||||
|
||||
// Copy assignment operator.
|
||||
VKVertexAttributeObject &operator=(const VKVertexAttributeObject &other);
|
||||
|
||||
void update_bindings(const VKContext &context, VKBatch &batch);
|
||||
void update_bindings(VKImmediate &immediate);
|
||||
|
||||
private:
|
||||
void update_bindings(const GPUVertFormat &vertex_format,
|
||||
VKVertexBuffer *vertex_buffer,
|
||||
VKBuffer *immediate_vertex_buffer,
|
||||
const int64_t vertex_len,
|
||||
const VKShaderInterface &interface,
|
||||
AttributeMask &r_occupied_attributes,
|
||||
const bool use_instancing);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
|
@ -0,0 +1,193 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. All rights reserved. */
|
||||
|
||||
#include "vk_vertex_attribute_object_cache.hh"
|
||||
|
||||
#include "vk_shader_interface.hh"
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
VKVaoCache::VKVaoCache()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
VKVaoCache::~VKVaoCache()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void VKVaoCache::init()
|
||||
{
|
||||
context_ = VKContext::get();
|
||||
interface_ = nullptr;
|
||||
is_dynamic_vao_count = false;
|
||||
for (int i = 0; i < VK_GPU_VAO_STATIC_LEN; i++) {
|
||||
static_vaos.interfaces[i] = nullptr;
|
||||
static_vaos.vao_ids[i].is_valid = false;
|
||||
}
|
||||
base_instance_ = 0;
|
||||
vao_id_.is_valid = false;
|
||||
}
|
||||
|
||||
void VKVaoCache::insert(const VKShaderInterface *interface, VKVertexAttributeObject &vao)
|
||||
{
|
||||
|
||||
BLI_assert(vao.is_valid);
|
||||
|
||||
/* Now insert the cache. */
|
||||
if (!is_dynamic_vao_count) {
|
||||
int i; /* find first unused slot */
|
||||
for (i = 0; i < VK_GPU_VAO_STATIC_LEN; i++) {
|
||||
if (static_vaos.vao_ids[i].is_valid == false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < VK_GPU_VAO_STATIC_LEN) {
|
||||
static_vaos.interfaces[i] = interface;
|
||||
static_vaos.vao_ids[i] = vao;
|
||||
}
|
||||
else {
|
||||
/* Erase previous entries, they will be added back if drawn again. */
|
||||
for (int i = 0; i < VK_GPU_VAO_STATIC_LEN; i++) {
|
||||
if (static_vaos.interfaces[i] != nullptr) {
|
||||
static_vaos.vao_ids[i].clear();
|
||||
/* context_->vao_free(static_vaos.vao_ids[i]); */
|
||||
}
|
||||
}
|
||||
/* Not enough place switch to dynamic. */
|
||||
is_dynamic_vao_count = true;
|
||||
/* Init dynamic arrays and let the branch below set the values. */
|
||||
dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT;
|
||||
dynamic_vaos.interfaces = (const VKShaderInterface **)MEM_callocN(
|
||||
dynamic_vaos.count * sizeof(VKShaderInterface *), "dyn vaos interfaces");
|
||||
dynamic_vaos.vao_ids = (VKVertexAttributeObject *)MEM_callocN(
|
||||
dynamic_vaos.count * sizeof(VKVertexAttributeObject), "dyn vaos ids");
|
||||
for (int j = 0; j < dynamic_vaos.count; j++) {
|
||||
dynamic_vaos.interfaces[j] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dynamic_vao_count) {
|
||||
int i; /* find first unused slot */
|
||||
for (i = 0; i < dynamic_vaos.count; i++) {
|
||||
if (dynamic_vaos.vao_ids[i].is_valid == false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == dynamic_vaos.count) {
|
||||
/* Not enough place, realloc the array. */
|
||||
i = dynamic_vaos.count;
|
||||
dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT;
|
||||
dynamic_vaos.interfaces = (const VKShaderInterface **)MEM_recallocN(
|
||||
(void *)dynamic_vaos.interfaces, sizeof(VKShaderInterface *) * dynamic_vaos.count);
|
||||
dynamic_vaos.vao_ids = (VKVertexAttributeObject *)MEM_recallocN(
|
||||
dynamic_vaos.vao_ids, sizeof(VKVertexAttributeObject) * dynamic_vaos.count);
|
||||
for (int j = 0; j < GPU_BATCH_VAO_DYN_ALLOC_COUNT; j++) {
|
||||
dynamic_vaos.interfaces[dynamic_vaos.count - j - 1] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
dynamic_vaos.interfaces[i] = interface;
|
||||
dynamic_vaos.vao_ids[i] = vao;
|
||||
}
|
||||
}
|
||||
|
||||
void VKVaoCache::remove(const VKShaderInterface *interface)
|
||||
{
|
||||
const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : VK_GPU_VAO_STATIC_LEN;
|
||||
VKVertexAttributeObject *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids :
|
||||
static_vaos.vao_ids;
|
||||
|
||||
const VKShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
|
||||
static_vaos.interfaces;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (interfaces[i] == interface) {
|
||||
vaos[i].clear();
|
||||
interfaces[i] = nullptr;
|
||||
break; /* cannot have duplicates */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VKVaoCache::clear()
|
||||
{
|
||||
|
||||
const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : VK_GPU_VAO_STATIC_LEN;
|
||||
VKVertexAttributeObject *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids :
|
||||
static_vaos.vao_ids;
|
||||
/* Early out, nothing to free. */
|
||||
if (context_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO(fclem): Slow way. Could avoid multiple mutex lock here */
|
||||
for (int i = 0; i < count; i++) {
|
||||
vaos[i].clear();
|
||||
// context_->vao_free(vaos[i]);
|
||||
}
|
||||
|
||||
if (is_dynamic_vao_count) {
|
||||
MEM_freeN((void *)dynamic_vaos.interfaces);
|
||||
MEM_freeN((void *)dynamic_vaos.vao_ids);
|
||||
}
|
||||
|
||||
/* Reinit. */
|
||||
this->init();
|
||||
}
|
||||
|
||||
static VKVertexAttributeObject &lookup_interface(int64_t element_len,
|
||||
const VKShaderInterface **interfaces,
|
||||
VKVertexAttributeObject *vaos,
|
||||
const VKShaderInterface *interface)
|
||||
{
|
||||
for (int i = 0; i < element_len; i++) {
|
||||
if (interfaces[i] == interface) {
|
||||
return vaos[i];
|
||||
}
|
||||
}
|
||||
|
||||
static VKVertexAttributeObject vao = {};
|
||||
vao.is_valid = false;
|
||||
return vao;
|
||||
}
|
||||
|
||||
VKVertexAttributeObject &VKVaoCache::lookup(const VKShaderInterface *interface)
|
||||
{
|
||||
if (is_dynamic_vao_count) {
|
||||
return lookup_interface(
|
||||
dynamic_vaos.count, dynamic_vaos.interfaces, dynamic_vaos.vao_ids, interface);
|
||||
}
|
||||
else {
|
||||
return lookup_interface(
|
||||
VK_GPU_VAO_STATIC_LEN, static_vaos.interfaces, static_vaos.vao_ids, interface);
|
||||
}
|
||||
}
|
||||
|
||||
VKVertexAttributeObject &VKVaoCache::vao_get(GPUBatch * /*batch*/)
|
||||
{
|
||||
Shader *shader = VKContext::get()->shader;
|
||||
VKShaderInterface *interface = static_cast<VKShaderInterface *>(shader->interface);
|
||||
if (interface_ != interface) {
|
||||
interface_ = interface;
|
||||
};
|
||||
|
||||
vao_id_ = this->lookup(interface_);
|
||||
|
||||
if (!vao_id_.is_valid) {
|
||||
vao_id_.is_valid = true;
|
||||
this->insert(interface_, vao_id_);
|
||||
}
|
||||
|
||||
vao_id_.clear();
|
||||
vao_id_.is_valid = true;
|
||||
|
||||
return vao_id_;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
|
@ -0,0 +1,74 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2023 Blender Foundation. All rights reserved. */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_batch_private.hh"
|
||||
|
||||
#include "vk_context.hh"
|
||||
#include "vk_vertex_attribute_object.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
const uint32_t VK_GPU_VAO_STATIC_LEN = 3;
|
||||
|
||||
/**
|
||||
* VAO management: remembers all geometry state (vertex attribute bindings & element buffer)
|
||||
* for each shader interface. Start with a static number of VAO's and fallback to dynamic count
|
||||
* if necessary. Once a batch goes dynamic it does not go back.
|
||||
*/
|
||||
class VKVaoCache {
|
||||
|
||||
private:
|
||||
/** Context for which the vao_cache_ was generated. */
|
||||
VKContext *context_ = nullptr;
|
||||
|
||||
/* TODO: why are these needed? */
|
||||
/** Last interface this batch was drawn with. */
|
||||
VKShaderInterface *interface_ = nullptr;
|
||||
/** Cached VAO for the last interface. */
|
||||
VKVertexAttributeObject vao_id_ = {};
|
||||
|
||||
int base_instance_ = 0;
|
||||
|
||||
bool is_dynamic_vao_count = false;
|
||||
union {
|
||||
/** Static handle count */
|
||||
struct {
|
||||
const VKShaderInterface *interfaces[VK_GPU_VAO_STATIC_LEN];
|
||||
VKVertexAttributeObject vao_ids[VK_GPU_VAO_STATIC_LEN];
|
||||
} static_vaos;
|
||||
/** Dynamic handle count */
|
||||
struct {
|
||||
uint32_t count;
|
||||
const VKShaderInterface **interfaces;
|
||||
VKVertexAttributeObject *vao_ids;
|
||||
} dynamic_vaos;
|
||||
};
|
||||
|
||||
public:
|
||||
bool is_dirty = false;
|
||||
VKVaoCache();
|
||||
~VKVaoCache();
|
||||
|
||||
VKVertexAttributeObject &vao_get(GPUBatch *batch);
|
||||
|
||||
/**
|
||||
* Return nullptr on cache miss (invalid VAO).
|
||||
*/
|
||||
VKVertexAttributeObject &lookup(const VKShaderInterface *interface);
|
||||
/**
|
||||
* Create a new VAO object and store it in the cache.
|
||||
*/
|
||||
void insert(const VKShaderInterface *interface, VKVertexAttributeObject &vao_id);
|
||||
void remove(const VKShaderInterface *interface);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
|
@ -53,6 +53,7 @@ void VKVertexBuffer::acquire_data()
|
|||
}
|
||||
|
||||
/* Discard previous data if any. */
|
||||
/* TODO: Use mapped memory. */
|
||||
MEM_SAFE_FREE(data);
|
||||
data = (uchar *)MEM_mallocN(sizeof(uchar) * this->size_alloc_get(), __func__);
|
||||
}
|
||||
|
@ -64,7 +65,23 @@ void VKVertexBuffer::release_data()
|
|||
MEM_SAFE_FREE(data);
|
||||
}
|
||||
|
||||
void VKVertexBuffer::upload_data() {}
|
||||
void VKVertexBuffer::upload_data()
|
||||
{
|
||||
VKContext &context = *VKContext::get();
|
||||
if (!buffer_.is_allocated()) {
|
||||
allocate(context);
|
||||
}
|
||||
|
||||
if (flag &= GPU_VERTBUF_DATA_DIRTY) {
|
||||
buffer_.update(data);
|
||||
if (usage_ == GPU_USAGE_STATIC) {
|
||||
MEM_SAFE_FREE(data);
|
||||
}
|
||||
|
||||
flag &= ~GPU_VERTBUF_DATA_DIRTY;
|
||||
flag |= GPU_VERTBUF_DATA_UPLOADED;
|
||||
}
|
||||
}
|
||||
|
||||
void VKVertexBuffer::duplicate_data(VertBuf * /*dst*/) {}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ class VKVertexBuffer : public VertBuf {
|
|||
|
||||
VkBuffer vk_handle() const
|
||||
{
|
||||
BLI_assert(buffer_.is_allocated());
|
||||
return buffer_.vk_handle();
|
||||
}
|
||||
|
||||
|
@ -42,4 +43,9 @@ class VKVertexBuffer : public VertBuf {
|
|||
void allocate(VKContext &context);
|
||||
};
|
||||
|
||||
static inline VKVertexBuffer *unwrap(VertBuf *vertex_buffer)
|
||||
{
|
||||
return static_cast<VKVertexBuffer *>(vertex_buffer);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -232,7 +232,12 @@ static PyObject *pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self),
|
|||
}
|
||||
|
||||
if (GPU_context_active_get()) {
|
||||
ofs = GPU_offscreen_create(width, height, true, pygpu_textureformat.value_found, err_out);
|
||||
ofs = GPU_offscreen_create(width,
|
||||
height,
|
||||
true,
|
||||
pygpu_textureformat.value_found,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_HOST_READ,
|
||||
err_out);
|
||||
}
|
||||
else {
|
||||
STRNCPY(err_out, "No active GPU context found");
|
||||
|
|
|
@ -685,7 +685,7 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_
|
|||
* depth or multisample buffers. 3D view creates own buffers with
|
||||
* the data it needs. */
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(
|
||||
region->winx, region->winy, false, GPU_RGBA8, NULL);
|
||||
region->winx, region->winy, false, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, NULL);
|
||||
if (!offscreen) {
|
||||
WM_report(RPT_ERROR, "Region could not be drawn!");
|
||||
return;
|
||||
|
@ -1151,7 +1151,8 @@ static void wm_draw_window(bContext *C, wmWindow *win)
|
|||
* stereo methods, but it's less efficient than drawing directly. */
|
||||
const int width = WM_window_pixels_x(win);
|
||||
const int height = WM_window_pixels_y(win);
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(width, height, false, GPU_RGBA8, NULL);
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(
|
||||
width, height, false, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, NULL);
|
||||
|
||||
if (offscreen) {
|
||||
GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
|
||||
|
@ -1226,7 +1227,8 @@ uint *WM_window_pixels_read_offscreen(bContext *C, wmWindow *win, int r_size[2])
|
|||
r_size[0] = WM_window_pixels_x(win);
|
||||
r_size[1] = WM_window_pixels_y(win);
|
||||
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(r_size[0], r_size[1], false, GPU_RGBA8, NULL);
|
||||
GPUOffScreen *offscreen = GPU_offscreen_create(
|
||||
r_size[0], r_size[1], false, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, NULL);
|
||||
if (UNLIKELY(!offscreen)) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1388,7 +1388,7 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
|
|||
BLI_assert(format != GPU_R8);
|
||||
|
||||
offscreen = vp->offscreen = GPU_offscreen_create(
|
||||
draw_view->width, draw_view->height, true, format, err_out);
|
||||
draw_view->width, draw_view->height, true, format, GPU_TEXTURE_USAGE_SHADER_READ, err_out);
|
||||
if (offscreen) {
|
||||
viewport = vp->viewport = GPU_viewport_create();
|
||||
if (!viewport) {
|
||||
|
|
Loading…
Reference in New Issue