GL: Add subpass input emulation #112051

Merged
Clément Foucault merged 10 commits from fclem/blender:gl-subpass-emulation into main 2023-10-01 15:27:29 +02:00
16 changed files with 424 additions and 22 deletions

View File

@ -12,13 +12,63 @@
extern "C" {
#endif
/**
* Describes the load operation of a framebuffer attachment at the start of a render pass.
*/
typedef enum eGPULoadOp {
/**
* Clear the framebuffer attachment using the clear value.
*/
GPU_LOADACTION_CLEAR = 0,
fclem marked this conversation as resolved Outdated

Different name, layout tells more about the order of data on the GPU inside the image. Perhaps GPUAttachmentUsage

Different name, layout tells more about the order of data on the GPU inside the image. Perhaps GPUAttachmentUsage
/**
* Load the value from the attached texture.
* Cannot be used with memoryless attachments.
* Slower than `GPU_LOADACTION_CLEAR` or `GPU_LOADACTION_DONT_CARE`.
*/
GPU_LOADACTION_LOAD,
GPU_LOADACTION_DONT_CARE
/**
* Do not care about the content of the attachment when the render pass starts.
* Useful if only the values being written are important.
* Faster than `GPU_LOADACTION_CLEAR`.
*/
GPU_LOADACTION_DONT_CARE,
} eGPULoadOp;
typedef enum eGPUStoreOp { GPU_STOREACTION_STORE = 0, GPU_STOREACTION_DONT_CARE } eGPUStoreOp;
/**
* Describes the store operation of a framebuffer attachment at the end of a render pass.
*/
typedef enum eGPUStoreOp {
/**
* Do not care about the content of the attachment when the render pass ends.
fclem marked this conversation as resolved Outdated

when the render pass ends

`when the render pass ends`
* Useful if only the values being written are important.
* Cannot be used with memoryless attachments.
*/
GPU_STOREACTION_STORE = 0,
/**
* The result of the rendering for this attachment will be discarded.
* No writes to the texture memory will be done which makes it faster than
* `GPU_STOREACTION_STORE`.
* IMPORTANT: The actual values of the attachment is to be considered undefined.
* Only to be used on transient attachment that are only used within the boundaries of
* a render pass (ex.: Uneeded depth buffer result).
*/
GPU_STOREACTION_DONT_CARE,
} eGPUStoreOp;
/**
* Describes the state of a framebuffer attachment during a sub-pass.
*
* NOTE: Until this is correctly implemented in all backend, reading and writing from the
* same attachment will not work. Although there is no case where it would currently be useful.
*/
typedef enum GPUAttachmentState {
/** Attachment will not be written during rendering. */
GPU_ATTACHEMENT_IGNORE = 0,
/** Attachment will be written during render sub-pass. This also works with blending. */
fclem marked this conversation as resolved Outdated

geometry passes?

geometry passes?
GPU_ATTACHEMENT_WRITE,
/** Attachment is used as input in the fragment shader. Incompatible with depth on Metal. */
GPU_ATTACHEMENT_READ,
} GPUAttachmentState;
typedef enum eGPUFrontFace {
GPU_CLOCKWISE,

View File

@ -178,6 +178,33 @@ void GPU_framebuffer_bind_loadstore(GPUFrameBuffer *framebuffer,
GPU_framebuffer_bind_loadstore(_fb, actions, (sizeof(actions) / sizeof(GPULoadStore))); \
}
/**
* Sub-pass config array matches attachment structure of `GPU_framebuffer_config_array`.
* This allows to explicitly specify attachment state within the next sub-pass.
* This enables a number of bandwidth optimizations specially on Tile Based Deferred Renderers
* where the attachments can be kept into tile memory and used in place for later sub-passes.
*
* Example:
* \code{.c}
* GPU_framebuffer_bind_loadstore(&fb, {
* GPU_ATTACHEMENT_WRITE, // must be depth buffer
* GPU_ATTACHEMENT_READ, // Color attachment 0
* GPU_ATTACHEMENT_IGNORE, // Color attachment 1
* GPU_ATTACHEMENT_WRITE} // Color attachment 2
* })
* \endcode
*/
void GPU_framebuffer_subpass_transition_array(GPUFrameBuffer *framebuffer,
const GPUAttachmentState *attachment_states,
uint attachment_len);
#define GPU_framebuffer_subpass_transition(_fb, ...) \
{ \
GPUAttachmentState actions[] = __VA_ARGS__; \
GPU_framebuffer_subpass_transition_array( \
_fb, actions, (sizeof(actions) / sizeof(GPUAttachmentState))); \
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -35,6 +35,9 @@ class DummyFrameBuffer : public FrameBuffer {
void attachment_set_loadstore_op(GPUAttachmentType /*type*/, GPULoadStore /*ls*/) override {}
void subpass_transition(const GPUAttachmentState /*depth_attachment_state*/,
Span<GPUAttachmentState> /*color_attachment_states*/) override{};
void read(eGPUFrameBufferBits /*planes*/,
eGPUDataFormat /*format*/,
const int /*area*/[4],

View File

@ -265,6 +265,14 @@ void GPU_framebuffer_bind_loadstore(GPUFrameBuffer *gpu_fb,
fb->load_store_config_array(load_store_actions, actions_len);
}
void GPU_framebuffer_subpass_transition_array(GPUFrameBuffer *gpu_fb,
const GPUAttachmentState *attachment_states,
uint attachment_len)
{
unwrap(gpu_fb)->subpass_transition(
attachment_states[0], Span<GPUAttachmentState>(attachment_states + 1, attachment_len - 1));
}
void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *gpu_fb)
{
const bool enable_srgb = false;

View File

@ -127,6 +127,9 @@ class FrameBuffer {
int dst_offset_x,
int dst_offset_y) = 0;
virtual void subpass_transition(const GPUAttachmentState depth_attachment_state,
Span<GPUAttachmentState> color_attachment_states) = 0;
void load_store_config_array(const GPULoadStore *load_store_actions, uint actions_len);
void attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment);

View File

@ -153,6 +153,9 @@ class MTLFrameBuffer : public FrameBuffer {
int dst_offset_x,
int dst_offset_y) override;
void subpass_transition(const GPUAttachmentState depth_attachment_state,
Span<GPUAttachmentState> color_attachment_states) override{};
void apply_state();
/* State. */

View File

@ -271,6 +271,8 @@ static void detect_workarounds()
/* Turn off vendor specific extensions. */
GLContext::native_barycentric_support = false;
GLContext::framebuffer_fetch_support = false;
GLContext::texture_barrier_support = false;
/* Do not alter OpenGL 4.3 features.
* These code paths should be removed. */
@ -510,6 +512,7 @@ bool GLContext::copy_image_support = false;
bool GLContext::debug_layer_support = false;
bool GLContext::direct_state_access_support = false;
bool GLContext::explicit_location_support = false;
bool GLContext::framebuffer_fetch_support = false;
bool GLContext::geometry_shader_invocations = false;
bool GLContext::fixed_restart_index_support = false;
bool GLContext::layered_rendering_support = false;
@ -519,6 +522,7 @@ bool GLContext::multi_bind_image_support = false;
bool GLContext::multi_draw_indirect_support = false;
bool GLContext::shader_draw_parameters_support = false;
bool GLContext::stencil_texturing_support = false;
bool GLContext::texture_barrier_support = false;
bool GLContext::texture_cube_map_array_support = false;
bool GLContext::texture_filter_anisotropic_support = false;
bool GLContext::texture_gather_support = false;
@ -609,6 +613,8 @@ void GLBackend::capabilities_init()
GLContext::explicit_location_support = epoxy_gl_version() >= 43;
GLContext::geometry_shader_invocations = epoxy_has_gl_extension("GL_ARB_gpu_shader5");
GLContext::fixed_restart_index_support = epoxy_has_gl_extension("GL_ARB_ES3_compatibility");
GLContext::framebuffer_fetch_support = epoxy_has_gl_extension("GL_EXT_shader_framebuffer_fetch");
GLContext::texture_barrier_support = epoxy_has_gl_extension("GL_ARB_texture_barrier");
GLContext::layered_rendering_support = epoxy_has_gl_extension(
"GL_ARB_shader_viewport_layer_array");
GLContext::native_barycentric_support = epoxy_has_gl_extension(
@ -628,6 +634,9 @@ void GLBackend::capabilities_init()
GLContext::vertex_attrib_binding_support = epoxy_has_gl_extension(
"GL_ARB_vertex_attrib_binding");
/* Disabled until it is proven to work. */
GLContext::framebuffer_fetch_support = false;
detect_workarounds();
/* Disable this feature entirely when not debugging. */

View File

@ -56,6 +56,7 @@ class GLContext : public Context {
static bool explicit_location_support;
static bool geometry_shader_invocations;
static bool fixed_restart_index_support;
static bool framebuffer_fetch_support;
static bool layered_rendering_support;
static bool native_barycentric_support;
static bool multi_bind_support;
@ -63,6 +64,7 @@ class GLContext : public Context {
static bool multi_draw_indirect_support;
static bool shader_draw_parameters_support;
static bool stencil_texturing_support;
static bool texture_barrier_support;
static bool texture_cube_map_array_support;
static bool texture_filter_anisotropic_support;
static bool texture_gather_support;

View File

@ -226,6 +226,82 @@ void GLFrameBuffer::update_attachments()
}
}
void GLFrameBuffer::subpass_transition(const GPUAttachmentState depth_attachment_state,
Span<GPUAttachmentState> color_attachment_states)
{
/* NOTE: Depth is not supported as input attachment because the Metal API doesn't support it and
* because depth is not compatible with the framebuffer fetch implementation. */
BLI_assert(depth_attachment_state != GPU_ATTACHEMENT_READ);
GPU_depth_mask(depth_attachment_state == GPU_ATTACHEMENT_WRITE);
bool any_read = false;
for (auto attachment : color_attachment_states.index_range()) {
if (attachment == GPU_ATTACHEMENT_READ) {
any_read = true;
break;
}
}
if (GLContext::framebuffer_fetch_support) {
if (any_read) {
glFramebufferFetchBarrierEXT();
}
}
else if (GLContext::texture_barrier_support) {
if (any_read) {
glTextureBarrier();
}
GLenum attachments[GPU_FB_MAX_COLOR_ATTACHMENT] = {GL_NONE};
for (int i : color_attachment_states.index_range()) {
GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i;
GPUTexture *attach_tex = this->attachments_[type].tex;
if (color_attachment_states[i] == GPU_ATTACHEMENT_READ) {
tmp_detached_[type] = this->attachments_[type]; /* Bypass feedback loop check. */
GPU_texture_bind_ex(attach_tex, GPUSamplerState::default_sampler(), i);
}
else {
tmp_detached_[type] = GPU_ATTACHMENT_NONE;
}
bool attach_write = color_attachment_states[i] == GPU_ATTACHEMENT_WRITE;
attachments[i] = (attach_tex && attach_write) ? to_gl(type) : GL_NONE;
}
/* We have to use `glDrawBuffers` instead of `glColorMaski` because the later is overwritten
* by the `GLStateManager`. */
/* WATCH(fclem): This modifies the frame-buffer state without setting `dirty_attachments_`. */
glDrawBuffers(ARRAY_SIZE(attachments), attachments);
}
else {
/* The only way to have correct visibility without extensions and ensure defined behavior, is
* to unbind the textures and update the frame-buffer. This is a slow operation but that's all
* we can do to emulate the sub-pass input. */
/* TODO(fclem): Could avoid the framebuffer reconfiguration by creating multiple framebuffers
* internally. */
for (int i : color_attachment_states.index_range()) {
GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i;
if (color_attachment_states[i] == GPU_ATTACHEMENT_WRITE) {
if (tmp_detached_[type].tex != nullptr) {
/* Re-attach previous read attachments. */
this->attachment_set(type, tmp_detached_[type]);
tmp_detached_[type] = GPU_ATTACHMENT_NONE;
}
}
else {
tmp_detached_[type] = this->attachments_[type];
unwrap(tmp_detached_[type].tex)->detach_from(this);
}
if (color_attachment_states[i] == GPU_ATTACHEMENT_READ) {
GPU_texture_bind_ex(tmp_detached_[type].tex, GPUSamplerState::default_sampler(), i);
}
}
if (dirty_attachments_) {
this->update_attachments();
}
}
}
void GLFrameBuffer::apply_state()
{
if (dirty_state_ == false) {

View File

@ -34,6 +34,8 @@ class GLFrameBuffer : public FrameBuffer {
GLStateManager *state_manager_ = nullptr;
/** Copy of the GL state. Contains ONLY color attachments enums for slot binding. */
GLenum gl_attachments_[GPU_FB_MAX_COLOR_ATTACHMENT] = {0};
/** List of attachment that are associated with this frame-buffer but temporarily detached. */
GPUAttachment tmp_detached_[GPU_FB_MAX_ATTACHMENT];
/** Internal frame-buffers are immutable. */
bool immutable_ = false;
/** True is the frame-buffer has its first color target using the GPU_SRGB8_A8 format. */
@ -79,6 +81,9 @@ class GLFrameBuffer : public FrameBuffer {
/* Attachment load-stores are currently no-op's in OpenGL. */
void attachment_set_loadstore_op(GPUAttachmentType /*type*/, GPULoadStore /*ls*/) override{};
void subpass_transition(const GPUAttachmentState depth_attachment_state,
Span<GPUAttachmentState> color_attachment_states) override;
void read(eGPUFrameBufferBits planes,
eGPUDataFormat format,
const int area[4],

View File

@ -110,9 +110,139 @@ static const char *to_string(const Type &type)
return "ivec4";
case Type::BOOL:
return "bool";
default:
return "unknown";
/* Alias special types. */
case Type::UCHAR:
case Type::USHORT:
return "uint";
case Type::UCHAR2:
case Type::USHORT2:
return "uvec2";
case Type::UCHAR3:
case Type::USHORT3:
return "uvec3";
case Type::UCHAR4:
case Type::USHORT4:
return "uvec4";
case Type::CHAR:
case Type::SHORT:
return "int";
case Type::CHAR2:
case Type::SHORT2:
return "ivec2";
case Type::CHAR3:
case Type::SHORT3:
return "ivec3";
case Type::CHAR4:
case Type::SHORT4:
return "ivec4";
case Type::VEC3_101010I2:
return "vec3";
}
BLI_assert_unreachable();
return "unknown";
}
static const int to_component_count(const Type &type)
{
switch (type) {
case Type::FLOAT:
case Type::UINT:
case Type::INT:
case Type::BOOL:
return 1;
case Type::VEC2:
case Type::UVEC2:
case Type::IVEC2:
return 2;
case Type::VEC3:
case Type::UVEC3:
case Type::IVEC3:
return 3;
case Type::VEC4:
case Type::UVEC4:
case Type::IVEC4:
return 4;
case Type::MAT3:
return 9;
case Type::MAT4:
return 16;
/* Alias special types. */
case Type::UCHAR:
case Type::USHORT:
return 1;
case Type::UCHAR2:
case Type::USHORT2:
return 2;
case Type::UCHAR3:
case Type::USHORT3:
return 3;
case Type::UCHAR4:
case Type::USHORT4:
return 4;
case Type::CHAR:
case Type::SHORT:
return 1;
case Type::CHAR2:
case Type::SHORT2:
return 2;
case Type::CHAR3:
case Type::SHORT3:
return 3;
case Type::CHAR4:
case Type::SHORT4:
return 4;
case Type::VEC3_101010I2:
return 3;
}
BLI_assert_unreachable();
return -1;
}
static const Type to_component_type(const Type &type)
{
switch (type) {
case Type::FLOAT:
case Type::VEC2:
case Type::VEC3:
case Type::VEC4:
case Type::MAT3:
case Type::MAT4:
return Type::FLOAT;
case Type::UINT:
case Type::UVEC2:
case Type::UVEC3:
case Type::UVEC4:
return Type::UINT;
case Type::INT:
case Type::IVEC2:
case Type::IVEC3:
case Type::IVEC4:
case Type::BOOL:
return Type::INT;
/* Alias special types. */
case Type::UCHAR:
case Type::UCHAR2:
case Type::UCHAR3:
case Type::UCHAR4:
case Type::USHORT:
case Type::USHORT2:
case Type::USHORT3:
case Type::USHORT4:
return Type::UINT;
case Type::CHAR:
case Type::CHAR2:
case Type::CHAR3:
case Type::CHAR4:
case Type::SHORT:
case Type::SHORT2:
case Type::SHORT3:
case Type::SHORT4:
return Type::INT;
case Type::VEC3_101010I2:
fclem marked this conversation as resolved

This need to be supported for more versatility. However, this will not work with imageLoad because we need to know the format of the image upfront. So I'll change the workaround to use sampler instead of image.

This need to be supported for more versatility. However, this will not work with `imageLoad` because we need to know the format of the image upfront. So I'll change the workaround to use `sampler` instead of `image`.
return Type::FLOAT;
}
BLI_assert_unreachable();
return Type::FLOAT;
}
static const char *to_string(const eGPUTextureFormat &type)
@ -581,7 +711,7 @@ std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) con
std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) const
{
std::stringstream ss;
std::string pre_main;
std::string pre_main, post_main;
ss << "\n/* Interfaces. */\n";
const Vector<StageInterfaceInfo *> &in_interfaces = info.geometry_source_.is_empty() ?
@ -605,7 +735,6 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
ss << "#define gpu_position_at_vertex(v) gpu_pos[v]\n";
}
else if (epoxy_has_gl_extension("GL_AMD_shader_explicit_vertex_parameter")) {
std::cout << "native" << std::endl;
/* NOTE(fclem): This won't work with geometry shader. Hopefully, we don't need geometry
* shader workaround if this extension/feature is detected. */
ss << "\n/* Stable Barycentric Coordinates. */\n";
@ -638,12 +767,63 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
if (epoxy_has_gl_extension("GL_ARB_conservative_depth")) {
ss << "layout(" << to_string(info.depth_write_) << ") out float gl_FragDepth;\n";
}
ss << "\n/* Sub-pass Inputs. */\n";
for (const ShaderCreateInfo::SubpassIn &input : info.subpass_inputs_) {
/* TODO(fclem): Add GL_EXT_shader_framebuffer_fetch support and fallback using imageLoad.
* For now avoid compilation failure. */
ss << "const " << to_string(input.type) << " " << input.name << " = " << to_string(input.type)
<< "(0);\n";
if (GLContext::framebuffer_fetch_support) {
/* Declare as inout but do not write to it. */
ss << "layout(location = " << std::to_string(input.index) << ") inout "
<< to_string(input.type) << " " << input.name << ";\n";
}
else {
std::string image_name = "gpu_subpass_img_";
image_name += std::to_string(input.index);
/* Declare global for input. */
ss << to_string(input.type) << " " << input.name << ";\n";
/* IMPORTANT: We assume that the frame-buffer will be layered or not based on the layer
* built-in flag. */
bool is_layered_fb = bool(info.builtins_ & BuiltinBits::LAYER);
/* Start with invalid value to detect failure cases. */
ImageType image_type = ImageType::FLOAT_BUFFER;
switch (to_component_type(input.type)) {
case Type::FLOAT:
image_type = is_layered_fb ? ImageType::FLOAT_2D_ARRAY : ImageType::FLOAT_2D;
break;
case Type::INT:
image_type = is_layered_fb ? ImageType::INT_2D_ARRAY : ImageType::INT_2D;
break;
case Type::UINT:
image_type = is_layered_fb ? ImageType::UINT_2D_ARRAY : ImageType::UINT_2D;
break;
default:
break;
}
/* Declare image. */
using Resource = ShaderCreateInfo::Resource;
/* NOTE(fclem): Using the attachment index as resource index might be problematic as it might
* collide with other resources. */
Resource res(Resource::BindType::SAMPLER, input.index);
res.sampler.type = image_type;
res.sampler.sampler = GPUSamplerState::default_sampler();
res.sampler.name = image_name;
print_resource(ss, res, false);
char swizzle[] = "xyzw";
swizzle[to_component_count(input.type)] = '\0';
std::string texel_co = (is_layered_fb) ? "ivec3(gl_FragCoord.xy, gpu_Layer)" :
"ivec2(gl_FragCoord.xy)";
std::stringstream ss_pre;
/* Populate the global before main using imageLoad. */
ss_pre << " " << input.name << " = texelFetch(" << image_name << ", " << texel_co << ", 0)."
<< swizzle << ";\n";
pre_main += ss_pre.str();
}
}
ss << "\n/* Outputs. */\n";
for (const ShaderCreateInfo::FragOut &output : info.fragment_outputs_) {
@ -663,8 +843,7 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
}
ss << "\n";
if (pre_main.empty() == false) {
std::string post_main;
if (!pre_main.empty() || !post_main.empty()) {
ss << main_function_wrapper(pre_main, post_main);
}
return ss.str();
@ -891,6 +1070,9 @@ static char *glsl_patch_default_get()
if (GLContext::native_barycentric_support) {
STR_CONCAT(patch, slen, "#extension GL_AMD_shader_explicit_vertex_parameter: enable\n");
}
if (GLContext::framebuffer_fetch_support) {
STR_CONCAT(patch, slen, "#extension GL_EXT_shader_framebuffer_fetch: enable\n");
}
/* Fallbacks. */
if (!GLContext::shader_draw_parameters_support) {

View File

@ -31,6 +31,8 @@ class GLShaderInterface : public ShaderInterface {
private:
/** Reference to VaoCaches using this interface */
Vector<GLVaoCache *> refs_;
/** Bitmask of color attachments to bind as images for sub-pass input emulation. */
uint8_t subpass_inputs_ = 0u;
public:
GLShaderInterface(GLuint program, const shader::ShaderCreateInfo &info);

View File

@ -817,7 +817,10 @@ void GLTexture::check_feedback_loop()
if (fb_[i] == fb) {
GPUAttachmentType type = fb_attachment_[i];
GPUAttachment attachment = fb->attachments_[type];
if (attachment.mip <= mip_max_ && attachment.mip >= mip_min_) {
/* Check for when texture is used with texture barrier. */
GPUAttachment attachment_read = fb->tmp_detached_[type];
if (attachment.mip <= mip_max_ && attachment.mip >= mip_min_ &&
attachment_read.tex == nullptr) {
char msg[256];
SNPRINTF(msg,
"Feedback loop: Trying to bind a texture (%s) with mip range %d-%d but mip %d is "

View File

@ -336,12 +336,15 @@ static void test_framebuffer_subpass_input()
const int2 size(1, 1);
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ;
GPUTexture *texture = GPU_texture_create_2d(
GPUTexture *texture_a = GPU_texture_create_2d(
__func__, UNPACK2(size), 1, GPU_R32I, usage, nullptr);
GPUTexture *texture_b = GPU_texture_create_2d(
__func__, UNPACK2(size), 1, GPU_R32I, usage, nullptr);
GPUFrameBuffer *framebuffer = GPU_framebuffer_create(__func__);
GPU_framebuffer_ensure_config(&framebuffer,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(texture)});
GPU_framebuffer_ensure_config(
&framebuffer,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(texture_a), GPU_ATTACHMENT_TEXTURE(texture_b)});
GPU_framebuffer_bind(framebuffer);
const float4 clear_color(0.0f);
@ -361,7 +364,7 @@ static void test_framebuffer_subpass_input()
create_info_read.vertex_source("gpu_framebuffer_subpass_input_test.glsl");
create_info_read.fragment_source("gpu_framebuffer_subpass_input_test.glsl");
create_info_read.subpass_in(0, Type::INT, "in_value", 0);
create_info_read.fragment_out(0, Type::INT, "out_value");
create_info_read.fragment_out(1, Type::INT, "out_value");
GPUShader *shader_read = GPU_shader_create_from_info(
reinterpret_cast<GPUShaderCreateInfo *>(&create_info_read));
@ -373,10 +376,16 @@ static void test_framebuffer_subpass_input()
GPU_vertbuf_data_alloc(verts, 3);
GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_TRIS, verts, nullptr, GPU_BATCH_OWNS_VBO);
/* Metal Raster Order Group does not need that. */
GPU_framebuffer_subpass_transition(
framebuffer, {GPU_ATTACHEMENT_IGNORE, GPU_ATTACHEMENT_WRITE, GPU_ATTACHEMENT_IGNORE});
GPU_batch_set_shader(batch, shader_write);
GPU_batch_draw(batch);
/* TODO(fclem): Vulkan might want to introduce an explicit sync event here. */
/* Metal Raster Order Group does not need that. */
GPU_framebuffer_subpass_transition(
framebuffer, {GPU_ATTACHEMENT_IGNORE, GPU_ATTACHEMENT_READ, GPU_ATTACHEMENT_WRITE});
GPU_batch_set_shader(batch, shader_read);
GPU_batch_draw(batch);
@ -385,12 +394,17 @@ static void test_framebuffer_subpass_input()
GPU_finish();
int *read_data = static_cast<int *>(GPU_texture_read(texture, GPU_DATA_INT, 0));
EXPECT_EQ(*read_data, 0xDEADC0DE);
MEM_freeN(read_data);
int *read_data_a = static_cast<int *>(GPU_texture_read(texture_a, GPU_DATA_INT, 0));
EXPECT_EQ(*read_data_a, 0xDEADBEEF);
MEM_freeN(read_data_a);
int *read_data_b = static_cast<int *>(GPU_texture_read(texture_b, GPU_DATA_INT, 0));
EXPECT_EQ(*read_data_b, 0xDEADC0DE);
MEM_freeN(read_data_b);
GPU_framebuffer_free(framebuffer);
GPU_texture_free(texture);
GPU_texture_free(texture_a);
GPU_texture_free(texture_b);
GPU_shader_free(shader_write);
GPU_shader_free(shader_read);

View File

@ -213,6 +213,18 @@ void VKFrameBuffer::attachment_set_loadstore_op(GPUAttachmentType /*type*/, GPUL
/** \} */
/* -------------------------------------------------------------------- */
/** \name Sub-pass transition
* \{ */
void VKFrameBuffer::subpass_transition(const GPUAttachmentState /*depth_attachment_state*/,
Span<GPUAttachmentState> /*color_attachment_states*/)
{
NOT_YET_IMPLEMENTED;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read back
* \{ */

View File

@ -55,6 +55,9 @@ class VKFrameBuffer : public FrameBuffer {
void attachment_set_loadstore_op(GPUAttachmentType type, GPULoadStore /*ls*/) override;
void subpass_transition(const GPUAttachmentState depth_attachment_state,
Span<GPUAttachmentState> color_attachment_states) override;
fclem marked this conversation as resolved Outdated

  void subpass_transition(const GPUAttachmentState /*depth_attachment_state*/,
                          Span<GPUAttachmentState> /*color_attachment_states*/) override {
        NOT_YET_IMPLEMENTED
};
``` void subpass_transition(const GPUAttachmentState /*depth_attachment_state*/, Span<GPUAttachmentState> /*color_attachment_states*/) override { NOT_YET_IMPLEMENTED };
void read(eGPUFrameBufferBits planes,
eGPUDataFormat format,
const int area[4],