WIP: Vulkan: Initial Immediate Mode Support. #106954

Closed
Jeroen Bakker wants to merge 27 commits from Jeroen-Bakker:vulkan-immediate into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
41 changed files with 2161 additions and 109 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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*/)
{

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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*/) {}

View File

@ -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

View File

@ -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");

View File

@ -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;
}

View File

@ -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) {