Vulkan: Clearing Framebuffer + Scissors #106044
|
@ -832,6 +832,7 @@ if(WITH_GTESTS)
|
|||
set(TEST_SRC
|
||||
tests/gpu_testing.cc
|
||||
|
||||
tests/framebuffer_test.cc
|
||||
tests/index_buffer_test.cc
|
||||
tests/push_constants_test.cc
|
||||
tests/shader_test.cc
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "gpu_testing.hh"
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
namespace blender::gpu::tests {
|
||||
|
||||
static void test_framebuffer_clear_color_single_attachment()
|
||||
{
|
||||
const int2 size(10, 10);
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
|
||||
GPUTexture *texture = GPU_texture_create_2d(
|
||||
__func__, UNPACK2(size), 1, GPU_RGBA32F, usage, nullptr);
|
||||
|
||||
GPUFrameBuffer *framebuffer = GPU_framebuffer_create(__func__);
|
||||
GPU_framebuffer_ensure_config(&framebuffer,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(texture)});
|
||||
GPU_framebuffer_bind(framebuffer);
|
||||
|
||||
const float4 clear_color(0.1f, 0.2f, 0.5f, 1.0f);
|
||||
GPU_framebuffer_clear_color(framebuffer, clear_color);
|
||||
GPU_finish();
|
||||
|
||||
float4 *read_data = static_cast<float4 *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0));
|
||||
for (float4 pixel_color : Span<float4>(read_data, size.x * size.y)) {
|
||||
EXPECT_EQ(pixel_color, clear_color);
|
||||
}
|
||||
MEM_freeN(read_data);
|
||||
|
||||
GPU_framebuffer_free(framebuffer);
|
||||
GPU_texture_free(texture);
|
||||
}
|
||||
GPU_TEST(framebuffer_clear_color_single_attachment);
|
||||
|
||||
static void test_framebuffer_clear_color_multiple_attachments()
|
||||
{
|
||||
const int2 size(10, 10);
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
|
||||
GPUTexture *texture1 = GPU_texture_create_2d(
|
||||
__func__, UNPACK2(size), 1, GPU_RGBA32F, usage, nullptr);
|
||||
GPUTexture *texture2 = GPU_texture_create_2d(
|
||||
__func__, UNPACK2(size), 1, GPU_RGBA32UI, usage, nullptr);
|
||||
|
||||
GPUFrameBuffer *framebuffer = GPU_framebuffer_create(__func__);
|
||||
GPU_framebuffer_ensure_config(
|
||||
&framebuffer,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(texture1), GPU_ATTACHMENT_TEXTURE(texture2)});
|
||||
GPU_framebuffer_bind(framebuffer);
|
||||
|
||||
const float4 clear_color(0.1f, 0.2f, 0.5f, 1.0f);
|
||||
GPU_framebuffer_clear_color(framebuffer, clear_color);
|
||||
GPU_finish();
|
||||
|
||||
float4 *read_data1 = static_cast<float4 *>(GPU_texture_read(texture1, GPU_DATA_FLOAT, 0));
|
||||
for (float4 pixel_color : Span<float4>(read_data1, size.x * size.y)) {
|
||||
EXPECT_EQ(pixel_color, clear_color);
|
||||
}
|
||||
MEM_freeN(read_data1);
|
||||
|
||||
uint4 *read_data2 = static_cast<uint4 *>(GPU_texture_read(texture2, GPU_DATA_UINT, 0));
|
||||
uint4 clear_color_uint(1036831949, 1045220557, 1056964608, 1065353216);
|
||||
for (uint4 pixel_color : Span<uint4>(read_data2, size.x * size.y)) {
|
||||
EXPECT_EQ(pixel_color, clear_color_uint);
|
||||
}
|
||||
MEM_freeN(read_data2);
|
||||
|
||||
GPU_framebuffer_free(framebuffer);
|
||||
GPU_texture_free(texture1);
|
||||
GPU_texture_free(texture2);
|
||||
}
|
||||
GPU_TEST(framebuffer_clear_color_multiple_attachments);
|
||||
|
||||
static void test_framebuffer_clear_multiple_color_multiple_attachments()
|
||||
{
|
||||
const int2 size(10, 10);
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
|
||||
GPUTexture *texture1 = GPU_texture_create_2d(
|
||||
__func__, UNPACK2(size), 1, GPU_RGBA32F, usage, nullptr);
|
||||
GPUTexture *texture2 = GPU_texture_create_2d(
|
||||
__func__, UNPACK2(size), 1, GPU_RGBA32F, usage, nullptr);
|
||||
|
||||
GPUFrameBuffer *framebuffer = GPU_framebuffer_create(__func__);
|
||||
GPU_framebuffer_ensure_config(
|
||||
&framebuffer,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(texture1), GPU_ATTACHMENT_TEXTURE(texture2)});
|
||||
GPU_framebuffer_bind(framebuffer);
|
||||
|
||||
const float4 clear_color[2] = {float4(0.1f, 0.2f, 0.5f, 1.0f), float4(0.5f, 0.2f, 0.1f, 1.0f)};
|
||||
GPU_framebuffer_multi_clear(
|
||||
framebuffer, static_cast<const float(*)[4]>(static_cast<const void *>(clear_color)));
|
||||
GPU_finish();
|
||||
|
||||
float4 *read_data1 = static_cast<float4 *>(GPU_texture_read(texture1, GPU_DATA_FLOAT, 0));
|
||||
for (float4 pixel_color : Span<float4>(read_data1, size.x * size.y)) {
|
||||
EXPECT_EQ(pixel_color, clear_color[0]);
|
||||
}
|
||||
MEM_freeN(read_data1);
|
||||
|
||||
float4 *read_data2 = static_cast<float4 *>(GPU_texture_read(texture2, GPU_DATA_FLOAT, 0));
|
||||
for (float4 pixel_color : Span<float4>(read_data1, size.x * size.y)) {
|
||||
EXPECT_EQ(pixel_color, clear_color[1]);
|
||||
}
|
||||
MEM_freeN(read_data2);
|
||||
|
||||
GPU_framebuffer_free(framebuffer);
|
||||
GPU_texture_free(texture1);
|
||||
GPU_texture_free(texture2);
|
||||
}
|
||||
GPU_TEST(framebuffer_clear_multiple_color_multiple_attachments);
|
||||
|
||||
static void test_framebuffer_clear_depth()
|
||||
{
|
||||
const int2 size(10, 10);
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
|
||||
GPUTexture *texture = GPU_texture_create_2d(
|
||||
__func__, UNPACK2(size), 1, GPU_DEPTH_COMPONENT32F, usage, nullptr);
|
||||
|
||||
GPUFrameBuffer *framebuffer = GPU_framebuffer_create(__func__);
|
||||
GPU_framebuffer_ensure_config(&framebuffer, {GPU_ATTACHMENT_TEXTURE(texture)});
|
||||
GPU_framebuffer_bind(framebuffer);
|
||||
|
||||
const float clear_depth = 0.5f;
|
||||
GPU_framebuffer_clear_depth(framebuffer, clear_depth);
|
||||
GPU_finish();
|
||||
|
||||
float *read_data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0));
|
||||
for (float pixel_depth : Span<float>(read_data, size.x * size.y)) {
|
||||
EXPECT_EQ(pixel_depth, clear_depth);
|
||||
}
|
||||
MEM_freeN(read_data);
|
||||
|
||||
GPU_framebuffer_free(framebuffer);
|
||||
GPU_texture_free(texture);
|
||||
}
|
||||
GPU_TEST(framebuffer_clear_depth);
|
||||
|
||||
static void test_framebuffer_scissor_test()
|
||||
{
|
||||
const int2 size(128, 128);
|
||||
const int bar_size = 16;
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
|
||||
GPUTexture *texture = GPU_texture_create_2d(
|
||||
__func__, UNPACK2(size), 1, GPU_RGBA32F, usage, nullptr);
|
||||
|
||||
GPUFrameBuffer *framebuffer = GPU_framebuffer_create(__func__);
|
||||
GPU_framebuffer_ensure_config(&framebuffer,
|
||||
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(texture)});
|
||||
GPU_framebuffer_bind(framebuffer);
|
||||
|
||||
const float4 color1(0.0f);
|
||||
const float4 color2(0.5f);
|
||||
const float4 color3(1.0f);
|
||||
GPU_framebuffer_clear_color(framebuffer, color1);
|
||||
|
||||
GPU_scissor_test(true);
|
||||
for (int x = 0; x < size.x; x += 2 * bar_size) {
|
||||
GPU_scissor(x, 0, bar_size, size.y);
|
||||
GPU_framebuffer_clear_color(framebuffer, color2);
|
||||
}
|
||||
for (int y = 0; y < size.y; y += 2 * bar_size) {
|
||||
GPU_scissor(0, y, size.x, bar_size);
|
||||
GPU_framebuffer_clear_color(framebuffer, color3);
|
||||
}
|
||||
GPU_scissor_test(false);
|
||||
GPU_finish();
|
||||
|
||||
float4 *read_data = static_cast<float4 *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0));
|
||||
int offset = 0;
|
||||
for (float4 pixel_color : Span<float4>(read_data, size.x * size.y)) {
|
||||
int x = offset % size.x;
|
||||
int y = offset / size.x;
|
||||
int bar_x = x / bar_size;
|
||||
int bar_y = y / bar_size;
|
||||
|
||||
if (bar_y % 2 == 0) {
|
||||
EXPECT_EQ(pixel_color, color3);
|
||||
}
|
||||
else {
|
||||
if (bar_x % 2 == 0) {
|
||||
EXPECT_EQ(pixel_color, color2);
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(pixel_color, color1);
|
||||
}
|
||||
}
|
||||
|
||||
offset++;
|
||||
}
|
||||
MEM_freeN(read_data);
|
||||
|
||||
GPU_framebuffer_free(framebuffer);
|
||||
GPU_texture_free(texture);
|
||||
}
|
||||
GPU_TEST(framebuffer_scissor_test);
|
||||
|
||||
} // namespace blender::gpu::tests
|
|
@ -8,6 +8,7 @@
|
|||
#include "vk_command_buffer.hh"
|
||||
#include "vk_buffer.hh"
|
||||
#include "vk_context.hh"
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_memory.hh"
|
||||
#include "vk_pipeline.hh"
|
||||
#include "vk_texture.hh"
|
||||
|
@ -73,6 +74,21 @@ 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void VKCommandBuffer::end_render_pass(const VKFrameBuffer & /*framebuffer*/)
|
||||
{
|
||||
vkCmdEndRenderPass(vk_command_buffer_);
|
||||
}
|
||||
|
||||
void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
|
||||
const VkPipelineLayout vk_pipeline_layout,
|
||||
const VkShaderStageFlags vk_shader_stages)
|
||||
|
@ -98,7 +114,7 @@ void VKCommandBuffer::copy(VKBuffer &dst_buffer,
|
|||
{
|
||||
vkCmdCopyImageToBuffer(vk_command_buffer_,
|
||||
src_texture.vk_image_handle(),
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
src_texture.current_layout_get(),
|
||||
dst_buffer.vk_handle(),
|
||||
regions.size(),
|
||||
regions.data());
|
||||
|
@ -110,7 +126,7 @@ void VKCommandBuffer::copy(VKTexture &dst_texture,
|
|||
vkCmdCopyBufferToImage(vk_command_buffer_,
|
||||
src_buffer.vk_handle(),
|
||||
dst_texture.vk_image_handle(),
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
dst_texture.current_layout_get(),
|
||||
regions.size(),
|
||||
regions.data());
|
||||
}
|
||||
|
@ -128,6 +144,12 @@ void VKCommandBuffer::clear(VkImage vk_image,
|
|||
ranges.data());
|
||||
}
|
||||
|
||||
void VKCommandBuffer::clear(Span<VkClearAttachment> attachments, Span<VkClearRect> areas)
|
||||
{
|
||||
vkCmdClearAttachments(
|
||||
vk_command_buffer_, attachments.size(), attachments.data(), areas.size(), areas.data());
|
||||
}
|
||||
|
||||
void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages,
|
||||
VkPipelineStageFlags destination_stages)
|
||||
{
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
|
||||
namespace blender::gpu {
|
||||
class VKBuffer;
|
||||
class VKTexture;
|
||||
class VKPushConstants;
|
||||
class VKPipeline;
|
||||
class VKDescriptorSet;
|
||||
class VKFrameBuffer;
|
||||
class VKPipeline;
|
||||
class VKPushConstants;
|
||||
class VKTexture;
|
||||
|
||||
/** Command buffer to keep track of the life-time of a command buffer. */
|
||||
class VKCommandBuffer : NonCopyable, NonMovable {
|
||||
|
@ -39,6 +40,9 @@ class VKCommandBuffer : NonCopyable, NonMovable {
|
|||
void bind(const VKDescriptorSet &descriptor_set,
|
||||
const VkPipelineLayout vk_pipeline_layout,
|
||||
VkPipelineBindPoint bind_point);
|
||||
void begin_render_pass(const VKFrameBuffer &framebuffer);
|
||||
void end_render_pass(const VKFrameBuffer &framebuffer);
|
||||
|
||||
/**
|
||||
* Add a push constant command to the command buffer.
|
||||
*
|
||||
|
@ -61,6 +65,11 @@ class VKCommandBuffer : NonCopyable, NonMovable {
|
|||
VkImageLayout vk_image_layout,
|
||||
const VkClearColorValue &vk_clear_color,
|
||||
Span<VkImageSubresourceRange> ranges);
|
||||
|
||||
/**
|
||||
* Clear attachments of the active framebuffer.
|
||||
*/
|
||||
void clear(Span<VkClearAttachment> attachments, Span<VkClearRect> areas);
|
||||
void fill(VKBuffer &buffer, uint32_t data);
|
||||
|
||||
/**
|
||||
|
|
|
@ -307,4 +307,46 @@ VkComponentMapping to_vk_component_mapping(const eGPUTextureFormat /*format*/)
|
|||
return component_mapping;
|
||||
}
|
||||
|
||||
template<typename T> void copy_color(T dst[4], const T *src)
|
||||
{
|
||||
dst[0] = src[0];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[2];
|
||||
dst[3] = src[3];
|
||||
}
|
||||
|
||||
VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const void *data)
|
||||
{
|
||||
VkClearColorValue result = {0.0f};
|
||||
switch (format) {
|
||||
case GPU_DATA_FLOAT: {
|
||||
const float *float_data = static_cast<const float *>(data);
|
||||
copy_color<float>(result.float32, float_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case GPU_DATA_INT: {
|
||||
const int32_t *int_data = static_cast<const int32_t *>(data);
|
||||
copy_color<int32_t>(result.int32, int_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case GPU_DATA_UINT: {
|
||||
const uint32_t *uint_data = static_cast<const uint32_t *>(data);
|
||||
copy_color<uint32_t>(result.uint32, uint_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case GPU_DATA_HALF_FLOAT:
|
||||
case GPU_DATA_UBYTE:
|
||||
case GPU_DATA_UINT_24_8:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
case GPU_DATA_2_10_10_10_REV: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -24,5 +24,6 @@ VkFormat to_vk_format(const eGPUTextureFormat format);
|
|||
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);
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -51,7 +51,7 @@ VKContext::VKContext(void *ghost_window, void *ghost_context)
|
|||
VKBackend::capabilities_init(*this);
|
||||
|
||||
/* For off-screen contexts. Default frame-buffer is empty. */
|
||||
active_fb = back_left = new VKFrameBuffer("back_left");
|
||||
back_left = new VKFrameBuffer("back_left");
|
||||
}
|
||||
|
||||
VKContext::~VKContext()
|
||||
|
@ -71,19 +71,24 @@ void VKContext::activate()
|
|||
{
|
||||
if (ghost_window_) {
|
||||
VkImage image; /* TODO will be used for reading later... */
|
||||
VkFramebuffer framebuffer;
|
||||
VkFramebuffer vk_framebuffer;
|
||||
VkRenderPass render_pass;
|
||||
VkExtent2D extent;
|
||||
uint32_t fb_id;
|
||||
|
||||
GHOST_GetVulkanBackbuffer(
|
||||
(GHOST_WindowHandle)ghost_window_, &image, &framebuffer, &render_pass, &extent, &fb_id);
|
||||
(GHOST_WindowHandle)ghost_window_, &image, &vk_framebuffer, &render_pass, &extent, &fb_id);
|
||||
|
||||
/* Recreate the gpu::VKFrameBuffer wrapper after every swap. */
|
||||
if (has_active_framebuffer()) {
|
||||
deactivate_framebuffer();
|
||||
}
|
||||
delete back_left;
|
||||
|
||||
back_left = new VKFrameBuffer("back_left", framebuffer, render_pass, extent);
|
||||
active_fb = back_left;
|
||||
VKFrameBuffer *framebuffer = new VKFrameBuffer(
|
||||
"back_left", vk_framebuffer, render_pass, extent);
|
||||
back_left = framebuffer;
|
||||
framebuffer->bind(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +118,9 @@ void VKContext::flush()
|
|||
|
||||
void VKContext::finish()
|
||||
{
|
||||
if (has_active_framebuffer()) {
|
||||
deactivate_framebuffer();
|
||||
}
|
||||
command_buffer_.submit();
|
||||
}
|
||||
|
||||
|
@ -120,4 +128,28 @@ void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/)
|
|||
{
|
||||
}
|
||||
|
||||
void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer)
|
||||
{
|
||||
if (has_active_framebuffer()) {
|
||||
deactivate_framebuffer();
|
||||
}
|
||||
|
||||
BLI_assert(active_fb == nullptr);
|
||||
active_fb = &framebuffer;
|
||||
command_buffer_.begin_render_pass(framebuffer);
|
||||
}
|
||||
|
||||
bool VKContext::has_active_framebuffer() const
|
||||
{
|
||||
return active_fb != nullptr;
|
||||
}
|
||||
|
||||
void VKContext::deactivate_framebuffer()
|
||||
{
|
||||
BLI_assert(active_fb != nullptr);
|
||||
VKFrameBuffer *framebuffer = unwrap(active_fb);
|
||||
command_buffer_.end_render_pass(*framebuffer);
|
||||
active_fb = nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "vk_descriptor_pools.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKFrameBuffer;
|
||||
|
||||
class VKContext : public Context {
|
||||
private:
|
||||
|
@ -55,6 +56,9 @@ class VKContext : public Context {
|
|||
bool debug_capture_scope_begin(void *scope) override;
|
||||
void debug_capture_scope_end(void *scope) override;
|
||||
|
||||
void activate_framebuffer(VKFrameBuffer &framebuffer);
|
||||
void deactivate_framebuffer();
|
||||
|
||||
static VKContext *get(void)
|
||||
{
|
||||
return static_cast<VKContext *>(Context::get());
|
||||
|
@ -102,6 +106,8 @@ class VKContext : public Context {
|
|||
|
||||
private:
|
||||
void init_physical_device_limits();
|
||||
|
||||
bool has_active_framebuffer() const;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
#include "vk_framebuffer.hh"
|
||||
#include "vk_memory.hh"
|
||||
#include "vk_texture.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
|
@ -20,7 +22,7 @@ VKFrameBuffer::VKFrameBuffer(const char *name) : FrameBuffer(name)
|
|||
|
||||
VKFrameBuffer::VKFrameBuffer(const char *name,
|
||||
VkFramebuffer vk_framebuffer,
|
||||
VkRenderPass /*vk_render_pass*/,
|
||||
VkRenderPass vk_render_pass,
|
||||
VkExtent2D vk_extent)
|
||||
: FrameBuffer(name)
|
||||
{
|
||||
|
@ -30,6 +32,7 @@ VKFrameBuffer::VKFrameBuffer(const char *name,
|
|||
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;
|
||||
|
@ -39,8 +42,8 @@ VKFrameBuffer::VKFrameBuffer(const char *name,
|
|||
|
||||
VKFrameBuffer::~VKFrameBuffer()
|
||||
{
|
||||
if (!immutable_ && vk_framebuffer_ != VK_NULL_HANDLE) {
|
||||
vkDestroyFramebuffer(vk_device_, vk_framebuffer_, NULL);
|
||||
if (!immutable_) {
|
||||
render_pass_free();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +51,32 @@ VKFrameBuffer::~VKFrameBuffer()
|
|||
|
||||
void VKFrameBuffer::bind(bool /*enabled_srgb*/)
|
||||
{
|
||||
update_attachments();
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
context.activate_framebuffer(*this);
|
||||
}
|
||||
|
||||
VkRect2D VKFrameBuffer::vk_render_area_get() const
|
||||
{
|
||||
VkRect2D render_area = {};
|
||||
|
||||
if (scissor_test_get()) {
|
||||
int scissor_rect[4];
|
||||
scissor_get(scissor_rect);
|
||||
render_area.offset.x = scissor_rect[0];
|
||||
render_area.offset.y = scissor_rect[1];
|
||||
render_area.extent.width = scissor_rect[2];
|
||||
render_area.extent.height = scissor_rect[3];
|
||||
}
|
||||
else {
|
||||
render_area.offset.x = 0;
|
||||
render_area.offset.y = 0;
|
||||
render_area.extent.width = width_;
|
||||
render_area.extent.height = height_;
|
||||
}
|
||||
|
||||
return render_area;
|
||||
}
|
||||
|
||||
bool VKFrameBuffer::check(char /*err_out*/[256])
|
||||
|
@ -55,29 +84,110 @@ bool VKFrameBuffer::check(char /*err_out*/[256])
|
|||
return false;
|
||||
}
|
||||
|
||||
void VKFrameBuffer::clear(eGPUFrameBufferBits /*buffers*/,
|
||||
const float /*clear_col*/[4],
|
||||
float /*clear_depth*/,
|
||||
uint /*clear_stencil*/)
|
||||
void VKFrameBuffer::build_clear_attachments_depth_stencil(
|
||||
const eGPUFrameBufferBits buffers,
|
||||
float clear_depth,
|
||||
uint32_t clear_stencil,
|
||||
Vector<VkClearAttachment> &r_attachments) const
|
||||
{
|
||||
VkClearAttachment clear_attachment = {};
|
||||
clear_attachment.aspectMask = (buffers & GPU_DEPTH_BIT ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) |
|
||||
(buffers & GPU_STENCIL_BIT ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
|
||||
clear_attachment.clearValue.depthStencil.depth = clear_depth;
|
||||
clear_attachment.clearValue.depthStencil.stencil = clear_stencil;
|
||||
r_attachments.append(clear_attachment);
|
||||
}
|
||||
|
||||
void VKFrameBuffer::clear_multi(const float (*/*clear_col*/)[4])
|
||||
void VKFrameBuffer::build_clear_attachments_color(const float (*clear_colors)[4],
|
||||
const bool multi_clear_colors,
|
||||
Vector<VkClearAttachment> &r_attachments) const
|
||||
{
|
||||
int color_index = 0;
|
||||
for (int color_slot = 0; color_slot < GPU_FB_MAX_COLOR_ATTACHMENT; color_slot++) {
|
||||
const GPUAttachment &attachment = attachments_[GPU_FB_COLOR_ATTACHMENT0 + color_slot];
|
||||
if (attachment.tex == nullptr) {
|
||||
continue;
|
||||
}
|
||||
VkClearAttachment clear_attachment = {};
|
||||
clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
clear_attachment.colorAttachment = color_slot;
|
||||
eGPUDataFormat data_format = to_data_format(GPU_texture_format(attachment.tex));
|
||||
clear_attachment.clearValue.color = to_vk_clear_color_value(data_format,
|
||||
&clear_colors[color_index]);
|
||||
r_attachments.append(clear_attachment);
|
||||
|
||||
color_index += multi_clear_colors ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Clear
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::clear(const Vector<VkClearAttachment> &attachments) const
|
||||
{
|
||||
VkClearRect clear_rect = {};
|
||||
clear_rect.rect = vk_render_area_get();
|
||||
clear_rect.baseArrayLayer = 0;
|
||||
clear_rect.layerCount = 1;
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
VKCommandBuffer &command_buffer = context.command_buffer_get();
|
||||
command_buffer.clear(attachments, Span<VkClearRect>(&clear_rect, 1));
|
||||
}
|
||||
|
||||
void VKFrameBuffer::clear(const eGPUFrameBufferBits buffers,
|
||||
const float clear_color[4],
|
||||
float clear_depth,
|
||||
uint clear_stencil)
|
||||
{
|
||||
Vector<VkClearAttachment> attachments;
|
||||
if (buffers & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) {
|
||||
build_clear_attachments_depth_stencil(buffers, clear_depth, clear_stencil, attachments);
|
||||
}
|
||||
if (buffers & GPU_COLOR_BIT) {
|
||||
float clear_color_single[4];
|
||||
copy_v4_v4(clear_color_single, clear_color);
|
||||
build_clear_attachments_color(&clear_color_single, false, attachments);
|
||||
}
|
||||
clear(attachments);
|
||||
}
|
||||
|
||||
void VKFrameBuffer::clear_multi(const float (*clear_color)[4])
|
||||
{
|
||||
Vector<VkClearAttachment> attachments;
|
||||
build_clear_attachments_color(clear_color, true, attachments);
|
||||
clear(attachments);
|
||||
}
|
||||
|
||||
void VKFrameBuffer::clear_attachment(GPUAttachmentType /*type*/,
|
||||
eGPUDataFormat /*data_format*/,
|
||||
const void * /*clear_value*/)
|
||||
{
|
||||
/* Clearing of a single attachment was added to implement `clear_multi` in OpenGL. As
|
||||
* `clear_multi` is supported in Vulkan it isn't needed to implement this method.
|
||||
*/
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Load/Store operations
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::attachment_set_loadstore_op(GPUAttachmentType /*type*/,
|
||||
eGPULoadOp /*load_action*/,
|
||||
eGPUStoreOp /*store_action*/)
|
||||
{
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Read back
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::read(eGPUFrameBufferBits /*planes*/,
|
||||
eGPUDataFormat /*format*/,
|
||||
const int /*area*/[4],
|
||||
|
@ -87,6 +197,12 @@ void VKFrameBuffer::read(eGPUFrameBufferBits /*planes*/,
|
|||
{
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blit operations
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::blit_to(eGPUFrameBufferBits /*planes*/,
|
||||
int /*src_slot*/,
|
||||
FrameBuffer * /*dst*/,
|
||||
|
@ -96,4 +212,169 @@ void VKFrameBuffer::blit_to(eGPUFrameBufferBits /*planes*/,
|
|||
{
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Update attachments
|
||||
* \{ */
|
||||
|
||||
void VKFrameBuffer::update_attachments()
|
||||
{
|
||||
if (immutable_) {
|
||||
return;
|
||||
}
|
||||
if (!dirty_attachments_) {
|
||||
return;
|
||||
}
|
||||
|
||||
render_pass_free();
|
||||
render_pass_create();
|
||||
|
||||
dirty_attachments_ = false;
|
||||
}
|
||||
|
||||
void VKFrameBuffer::render_pass_create()
|
||||
{
|
||||
BLI_assert(!immutable_);
|
||||
BLI_assert(vk_render_pass_ == VK_NULL_HANDLE);
|
||||
BLI_assert(vk_framebuffer_ == VK_NULL_HANDLE);
|
||||
|
||||
VK_ALLOCATION_CALLBACKS
|
||||
|
||||
/* Track first attachment for size.*/
|
||||
GPUAttachmentType first_attachment = GPU_FB_MAX_ATTACHMENT;
|
||||
|
||||
std::array<VkAttachmentDescription, GPU_FB_MAX_ATTACHMENT> attachment_descriptions;
|
||||
std::array<VkImageView, GPU_FB_MAX_ATTACHMENT> image_views;
|
||||
std::array<VkAttachmentReference, GPU_FB_MAX_ATTACHMENT> attachment_references;
|
||||
/*Vector<VkAttachmentReference> color_attachments;
|
||||
VkAttachmentReference depth_attachment = {};
|
||||
*/
|
||||
bool has_depth_attachment = false;
|
||||
bool found_attachment = false;
|
||||
int depth_location = -1;
|
||||
|
||||
for (int type = GPU_FB_MAX_ATTACHMENT - 1; type >= 0; type--) {
|
||||
GPUAttachment &attachment = attachments_[type];
|
||||
if (attachment.tex == nullptr && !found_attachment) {
|
||||
/* Move the depth texture to the next binding point after all color textures. The binding
|
||||
* location of the color textures should be kept in sync between ShaderCreateInfos and the
|
||||
* framebuffer attachments. The depth buffer should be the last slot. */
|
||||
depth_location = max_ii(type - GPU_FB_COLOR_ATTACHMENT0, 0);
|
||||
continue;
|
||||
}
|
||||
found_attachment |= attachment.tex != nullptr;
|
||||
|
||||
/* Keep the first attachment to the first color attachment, or to the depth buffer when there
|
||||
* is no color attachment. */
|
||||
if (attachment.tex != nullptr &&
|
||||
(first_attachment == GPU_FB_MAX_ATTACHMENT || type >= GPU_FB_COLOR_ATTACHMENT0)) {
|
||||
first_attachment = static_cast<GPUAttachmentType>(type);
|
||||
}
|
||||
|
||||
int attachment_location = type >= GPU_FB_COLOR_ATTACHMENT0 ? type - GPU_FB_COLOR_ATTACHMENT0 :
|
||||
depth_location;
|
||||
|
||||
if (attachment.tex) {
|
||||
/* Ensure texture is allocated to ensure the image view.*/
|
||||
VKTexture &texture = *static_cast<VKTexture *>(unwrap(attachment.tex));
|
||||
texture.ensure_allocated();
|
||||
image_views[attachment_location] = texture.vk_image_view_handle();
|
||||
|
||||
VkAttachmentDescription &attachment_description =
|
||||
attachment_descriptions[attachment_location];
|
||||
attachment_description.flags = 0;
|
||||
attachment_description.format = to_vk_format(texture.format_get());
|
||||
attachment_description.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachment_description.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
attachment_description.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
|
||||
/* Create the attachment reference. */
|
||||
const bool is_depth_attachment = ELEM(
|
||||
type, GPU_FB_DEPTH_ATTACHMENT, GPU_FB_DEPTH_STENCIL_ATTACHMENT);
|
||||
|
||||
BLI_assert_msg(!is_depth_attachment || !has_depth_attachment,
|
||||
"There can only be one depth/stencil attachment.");
|
||||
has_depth_attachment |= is_depth_attachment;
|
||||
VkAttachmentReference &attachment_reference = attachment_references[attachment_location];
|
||||
attachment_reference.attachment = attachment_location;
|
||||
attachment_reference.layout = is_depth_attachment ?
|
||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL :
|
||||
VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the size, viewport & scissor based on the first attachment. */
|
||||
if (first_attachment != GPU_FB_MAX_ATTACHMENT) {
|
||||
GPUAttachment &attachment = attachments_[first_attachment];
|
||||
BLI_assert(attachment.tex);
|
||||
|
||||
int size[3];
|
||||
GPU_texture_get_mipmap_size(attachment.tex, attachment.mip, size);
|
||||
size_set(size[0], size[1]);
|
||||
}
|
||||
else {
|
||||
this->size_set(0, 0);
|
||||
}
|
||||
viewport_reset();
|
||||
scissor_reset();
|
||||
|
||||
/* Create render pass. */
|
||||
|
||||
const int attachment_len = has_depth_attachment ? depth_location + 1 : depth_location;
|
||||
const int color_attachment_len = depth_location;
|
||||
VkSubpassDescription subpass = {};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = color_attachment_len;
|
||||
subpass.pColorAttachments = attachment_references.begin();
|
||||
|
||||
if (has_depth_attachment) {
|
||||
subpass.pDepthStencilAttachment = &attachment_references[depth_location];
|
||||
}
|
||||
|
||||
VkRenderPassCreateInfo render_pass_info = {};
|
||||
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
render_pass_info.attachmentCount = attachment_len;
|
||||
render_pass_info.pAttachments = attachment_descriptions.data();
|
||||
render_pass_info.subpassCount = 1;
|
||||
render_pass_info.pSubpasses = &subpass;
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
vkCreateRenderPass(
|
||||
context.device_get(), &render_pass_info, vk_allocation_callbacks, &vk_render_pass_);
|
||||
|
||||
/* We might want to split framebuffer and render pass....*/
|
||||
VkFramebufferCreateInfo framebuffer_create_info = {};
|
||||
framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
framebuffer_create_info.renderPass = vk_render_pass_;
|
||||
framebuffer_create_info.attachmentCount = attachment_len;
|
||||
framebuffer_create_info.pAttachments = image_views.begin();
|
||||
framebuffer_create_info.width = width_;
|
||||
framebuffer_create_info.height = height_;
|
||||
framebuffer_create_info.layers = 1;
|
||||
|
||||
vkCreateFramebuffer(
|
||||
context.device_get(), &framebuffer_create_info, vk_allocation_callbacks, &vk_framebuffer_);
|
||||
}
|
||||
|
||||
void VKFrameBuffer::render_pass_free()
|
||||
{
|
||||
BLI_assert(!immutable_);
|
||||
if (vk_render_pass_ == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
VK_ALLOCATION_CALLBACKS
|
||||
|
||||
VKContext &context = *VKContext::get();
|
||||
vkDestroyRenderPass(context.device_get(), vk_render_pass_, vk_allocation_callbacks);
|
||||
vkDestroyFramebuffer(context.device_get(), vk_framebuffer_, vk_allocation_callbacks);
|
||||
vk_render_pass_ = VK_NULL_HANDLE;
|
||||
vk_framebuffer_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_framebuffer_private.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
@ -20,7 +24,7 @@ class VKFrameBuffer : public FrameBuffer {
|
|||
/* Vulkan device who created the handle. */
|
||||
VkDevice vk_device_ = VK_NULL_HANDLE;
|
||||
/* Base render pass used for framebuffer creation. */
|
||||
VkRenderPass render_pass_ = VK_NULL_HANDLE;
|
||||
VkRenderPass vk_render_pass_ = VK_NULL_HANDLE;
|
||||
/* Number of layers if the attachments are layered textures. */
|
||||
int depth_ = 1;
|
||||
/** Internal frame-buffers are immutable. */
|
||||
|
@ -46,10 +50,10 @@ class VKFrameBuffer : public FrameBuffer {
|
|||
void bind(bool enabled_srgb) override;
|
||||
bool check(char err_out[256]) override;
|
||||
void clear(eGPUFrameBufferBits buffers,
|
||||
const float clear_col[4],
|
||||
const float clear_color[4],
|
||||
float clear_depth,
|
||||
uint clear_stencil) override;
|
||||
void clear_multi(const float (*clear_col)[4]) override;
|
||||
void clear_multi(const float (*clear_color)[4]) override;
|
||||
void clear_attachment(GPUAttachmentType type,
|
||||
eGPUDataFormat data_format,
|
||||
const void *clear_value) override;
|
||||
|
@ -71,6 +75,39 @@ class VKFrameBuffer : public FrameBuffer {
|
|||
int dst_slot,
|
||||
int dst_offset_x,
|
||||
int dst_offset_y) override;
|
||||
|
||||
VkFramebuffer vk_framebuffer_get() const
|
||||
{
|
||||
BLI_assert(vk_framebuffer_ != VK_NULL_HANDLE);
|
||||
return vk_framebuffer_;
|
||||
}
|
||||
|
||||
VkRenderPass vk_render_pass_get() const
|
||||
{
|
||||
BLI_assert(vk_render_pass_ != VK_NULL_HANDLE);
|
||||
return vk_render_pass_;
|
||||
}
|
||||
VkRect2D vk_render_area_get() const;
|
||||
|
||||
private:
|
||||
void update_attachments();
|
||||
void render_pass_free();
|
||||
void render_pass_create();
|
||||
|
||||
/* Clearing attachments */
|
||||
void build_clear_attachments_depth_stencil(eGPUFrameBufferBits buffers,
|
||||
float clear_depth,
|
||||
uint32_t clear_stencil,
|
||||
Vector<VkClearAttachment> &r_attachments) const;
|
||||
void build_clear_attachments_color(const float (*clear_colors)[4],
|
||||
const bool multi_clear_colors,
|
||||
Vector<VkClearAttachment> &r_attachments) const;
|
||||
void clear(const Vector<VkClearAttachment> &attachments) const;
|
||||
};
|
||||
|
||||
static inline VKFrameBuffer *unwrap(FrameBuffer *framebuffer)
|
||||
{
|
||||
return static_cast<VKFrameBuffer *>(framebuffer);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -37,48 +37,6 @@ void VKTexture::copy_to(Texture * /*tex*/)
|
|||
{
|
||||
}
|
||||
|
||||
template<typename T> void copy_color(T dst[4], const T *src)
|
||||
{
|
||||
dst[0] = src[0];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[2];
|
||||
dst[3] = src[3];
|
||||
}
|
||||
|
||||
static VkClearColorValue to_vk_clear_color_value(eGPUDataFormat format, const void *data)
|
||||
{
|
||||
VkClearColorValue result = {{0.0f}};
|
||||
switch (format) {
|
||||
case GPU_DATA_FLOAT: {
|
||||
const float *float_data = static_cast<const float *>(data);
|
||||
copy_color<float>(result.float32, float_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case GPU_DATA_INT: {
|
||||
const int32_t *int_data = static_cast<const int32_t *>(data);
|
||||
copy_color<int32_t>(result.int32, int_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case GPU_DATA_UINT: {
|
||||
const uint32_t *uint_data = static_cast<const uint32_t *>(data);
|
||||
copy_color<uint32_t>(result.uint32, uint_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case GPU_DATA_HALF_FLOAT:
|
||||
case GPU_DATA_UBYTE:
|
||||
case GPU_DATA_UINT_24_8:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
case GPU_DATA_2_10_10_10_REV: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void VKTexture::clear(eGPUDataFormat format, const void *data)
|
||||
{
|
||||
if (!is_allocated()) {
|
||||
|
@ -92,9 +50,10 @@ void VKTexture::clear(eGPUDataFormat format, const void *data)
|
|||
range.aspectMask = to_vk_image_aspect_flag_bits(format_);
|
||||
range.levelCount = VK_REMAINING_MIP_LEVELS;
|
||||
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
||||
layout_ensure(context, VK_IMAGE_LAYOUT_GENERAL);
|
||||
|
||||
command_buffer.clear(
|
||||
vk_image_, VK_IMAGE_LAYOUT_GENERAL, clear_color, Span<VkImageSubresourceRange>(&range, 1));
|
||||
vk_image_, current_layout_get(), clear_color, Span<VkImageSubresourceRange>(&range, 1));
|
||||
}
|
||||
|
||||
void VKTexture::swizzle_set(const char /*swizzle_mask*/[4])
|
||||
|
@ -111,8 +70,10 @@ void VKTexture::mip_range_set(int /*min*/, int /*max*/)
|
|||
|
||||
void *VKTexture::read(int mip, eGPUDataFormat format)
|
||||
{
|
||||
/* Vulkan images cannot be directly mapped to host memory and requires a staging buffer. */
|
||||
VKContext &context = *VKContext::get();
|
||||
layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
/* Vulkan images cannot be directly mapped to host memory and requires a staging buffer. */
|
||||
VKBuffer staging_buffer;
|
||||
|
||||
/* NOTE: mip_size_get() won't override any dimension that is equal to 0. */
|
||||
|
@ -170,6 +131,7 @@ void VKTexture::update_sub(
|
|||
region.imageSubresource.mipLevel = mip;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
|
||||
layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
VKCommandBuffer &command_buffer = context.command_buffer_get();
|
||||
command_buffer.copy(*this, staging_buffer, Span<VkBufferImageCopy>(®ion, 1));
|
||||
command_buffer.submit();
|
||||
|
@ -208,11 +170,51 @@ bool VKTexture::init_internal(const GPUTexture * /*src*/, int /*mip_offset*/, in
|
|||
return false;
|
||||
}
|
||||
|
||||
bool VKTexture::is_allocated()
|
||||
void VKTexture::ensure_allocated()
|
||||
{
|
||||
if (!is_allocated()) {
|
||||
allocate();
|
||||
}
|
||||
}
|
||||
|
||||
bool VKTexture::is_allocated() const
|
||||
{
|
||||
return vk_image_ != VK_NULL_HANDLE && allocation_ != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
static VkImageUsageFlagBits to_vk_image_usage(const eGPUTextureUsage usage,
|
||||
const eGPUTextureFormatFlag format_flag)
|
||||
{
|
||||
VkImageUsageFlagBits result = static_cast<VkImageUsageFlagBits>(VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
if (usage & GPU_TEXTURE_USAGE_SHADER_READ) {
|
||||
result = static_cast<VkImageUsageFlagBits>(result | VK_IMAGE_USAGE_STORAGE_BIT);
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_SHADER_WRITE) {
|
||||
result = static_cast<VkImageUsageFlagBits>(result | VK_IMAGE_USAGE_STORAGE_BIT);
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_ATTACHMENT) {
|
||||
if (format_flag & (GPU_FORMAT_NORMALIZED_INTEGER | GPU_FORMAT_COMPRESSED)) {
|
||||
/* These formats aren't supported as an attachment. When using GPU_TEXTURE_USAGE_DEFAULT they
|
||||
* are still being evaluated to be attachable. So we need to skip them.*/
|
||||
}
|
||||
else {
|
||||
if (format_flag & (GPU_FORMAT_DEPTH | GPU_FORMAT_STENCIL)) {
|
||||
result = static_cast<VkImageUsageFlagBits>(result |
|
||||
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
|
||||
}
|
||||
else {
|
||||
result = static_cast<VkImageUsageFlagBits>(result | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_HOST_READ) {
|
||||
result = static_cast<VkImageUsageFlagBits>(result | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VKTexture::allocate()
|
||||
{
|
||||
BLI_assert(!is_allocated());
|
||||
|
@ -230,10 +232,14 @@ bool VKTexture::allocate()
|
|||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.format = to_vk_format(format_);
|
||||
image_info.tiling = VK_IMAGE_TILING_LINEAR;
|
||||
/* Some platforms (NVIDIA) requires that attached textures are always tiled optimal.
|
||||
*
|
||||
* As image data are always accessed via an staging buffer we can enable optimal tiling for all
|
||||
* texture. Tilings based on actual usages should be done in `VKFramebuffer`.
|
||||
*/
|
||||
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
image_info.usage = to_vk_image_usage(gpu_image_usage_flags_, format_flag_);
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkResult result;
|
||||
|
@ -254,8 +260,6 @@ bool VKTexture::allocate()
|
|||
|
||||
VmaAllocationCreateInfo allocCreateInfo = {};
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||
allocCreateInfo.flags = static_cast<VmaAllocationCreateFlagBits>(
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT);
|
||||
allocCreateInfo.priority = 1.0f;
|
||||
result = vmaCreateImage(context.mem_allocator_get(),
|
||||
&image_info,
|
||||
|
@ -268,15 +272,7 @@ bool VKTexture::allocate()
|
|||
}
|
||||
|
||||
/* Promote image to the correct layout. */
|
||||
VkImageMemoryBarrier barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
barrier.image = vk_image_;
|
||||
barrier.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(format_);
|
||||
barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
|
||||
barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
||||
context.command_buffer_get().pipeline_barrier(Span<VkImageMemoryBarrier>(&barrier, 1));
|
||||
layout_ensure(context, VK_IMAGE_LAYOUT_GENERAL);
|
||||
|
||||
VK_ALLOCATION_CALLBACKS
|
||||
VkImageViewCreateInfo image_view_info = {};
|
||||
|
@ -307,4 +303,37 @@ void VKTexture::image_bind(int binding)
|
|||
shader->pipeline_get().descriptor_set_get().image_bind(*this, location);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Image Layout
|
||||
* \{ */
|
||||
|
||||
VkImageLayout VKTexture::current_layout_get() const
|
||||
{
|
||||
return current_layout_;
|
||||
}
|
||||
|
||||
void VKTexture::current_layout_set(const VkImageLayout new_layout)
|
||||
{
|
||||
current_layout_ = new_layout;
|
||||
}
|
||||
|
||||
void VKTexture::layout_ensure(VKContext &context, const VkImageLayout requested_layout)
|
||||
{
|
||||
const VkImageLayout current_layout = current_layout_get();
|
||||
if (current_layout == requested_layout) {
|
||||
return;
|
||||
}
|
||||
VkImageMemoryBarrier barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.oldLayout = current_layout;
|
||||
barrier.newLayout = requested_layout;
|
||||
barrier.image = vk_image_;
|
||||
barrier.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(format_);
|
||||
barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
|
||||
barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
||||
context.command_buffer_get().pipeline_barrier(Span<VkImageMemoryBarrier>(&barrier, 1));
|
||||
current_layout_set(requested_layout);
|
||||
}
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -17,6 +17,12 @@ class VKTexture : public Texture {
|
|||
VkImageView vk_image_view_ = VK_NULL_HANDLE;
|
||||
VmaAllocation allocation_ = VK_NULL_HANDLE;
|
||||
|
||||
/* Last image layout of the texture. Framebuffer and barriers can alter/require the actual layout
|
||||
* to be changed. During this it requires to set the current layout in order to know which
|
||||
* conversion should happen. #current_layout_ keep track of the layout so the correct conversion
|
||||
* can be done.*/
|
||||
VkImageLayout current_layout_ = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
public:
|
||||
VKTexture(const char *name) : Texture(name)
|
||||
{
|
||||
|
@ -43,13 +49,17 @@ class VKTexture : public Texture {
|
|||
void image_bind(int location);
|
||||
VkImage vk_image_handle() const
|
||||
{
|
||||
BLI_assert(is_allocated());
|
||||
return vk_image_;
|
||||
}
|
||||
VkImageView vk_image_view_handle() const
|
||||
{
|
||||
BLI_assert(is_allocated());
|
||||
return vk_image_view_;
|
||||
}
|
||||
|
||||
void ensure_allocated();
|
||||
|
||||
protected:
|
||||
bool init_internal() override;
|
||||
bool init_internal(GPUVertBuf *vbo) override;
|
||||
|
@ -57,7 +67,8 @@ class VKTexture : public Texture {
|
|||
|
||||
private:
|
||||
/** Is this texture already allocated on device. */
|
||||
bool is_allocated();
|
||||
bool is_allocated() const;
|
||||
|
||||
/**
|
||||
* Allocate the texture of the device. Result is `true` when texture is successfully allocated
|
||||
* on the device.
|
||||
|
@ -65,6 +76,36 @@ class VKTexture : public Texture {
|
|||
bool allocate();
|
||||
|
||||
VkImageViewType vk_image_view_type() const;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Image Layout
|
||||
* \{ */
|
||||
public:
|
||||
/**
|
||||
* Update the current layout attribute, without actually changing the layout.
|
||||
*
|
||||
* Vulkan can change the layout of an image, when a command is being executed.
|
||||
* The start of a render pass or the end of a render pass can also alter the
|
||||
* actual layout of the image. This method allows to change the last known layout
|
||||
* that the image is using.
|
||||
*
|
||||
* NOTE: When we add command encoding, this should partly being done inside
|
||||
* the command encoder, as there is more accurate determination of the transition
|
||||
* of the layout. Only the final transition should then be stored inside the texture
|
||||
* to be used by as initial layout for the next set of commands.
|
||||
*/
|
||||
void current_layout_set(VkImageLayout new_layout);
|
||||
VkImageLayout current_layout_get() const;
|
||||
|
||||
/**
|
||||
* Ensure the layout of the texture. This also performs the conversion by adding a memory
|
||||
* barrier to the active command buffer to perform the conversion.
|
||||
*
|
||||
* When texture is already in the requested layout, nothing will be done.
|
||||
*/
|
||||
void layout_ensure(VKContext &context, VkImageLayout requested_layout);
|
||||
|
||||
/** \} */
|
||||
};
|
||||
|
||||
static inline VKTexture *unwrap(Texture *tex)
|
||||
|
|
Loading…
Reference in New Issue
In case of windows machine, this part does not pass.
attachment_references.data();
image_views.data();
No problem here.