The goal is to solve confusion of the "All rights reserved" for licensing
code under an open-source license.
The phrase "All rights reserved" comes from a historical convention that
required this phrase for the copyright protection to apply. This convention
is no longer relevant.
However, even though the phrase has no meaning in establishing the copyright
it has not lost meaning in terms of licensing.
This change makes it so code under the Blender Foundation copyright does
not use "all rights reserved". This is also how the GPL license itself
states how to apply it to the source code:
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software ...
This change does not change copyright notice in cases when the copyright
is dual (BF and an author), or just an author of the code. It also does
mot change copyright which is inherited from NaN Holding BV as it needs
some further investigation about what is the proper way to handle it.
381 lines
13 KiB
C++
381 lines
13 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2022 Blender Foundation */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#include "vk_framebuffer.hh"
|
|
#include "vk_memory.hh"
|
|
#include "vk_texture.hh"
|
|
|
|
namespace blender::gpu {
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Creation & Deletion
|
|
* \{ */
|
|
|
|
VKFrameBuffer::VKFrameBuffer(const char *name) : FrameBuffer(name)
|
|
{
|
|
immutable_ = false;
|
|
}
|
|
|
|
VKFrameBuffer::VKFrameBuffer(const char *name,
|
|
VkFramebuffer vk_framebuffer,
|
|
VkRenderPass vk_render_pass,
|
|
VkExtent2D vk_extent)
|
|
: FrameBuffer(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_;
|
|
}
|
|
|
|
VKFrameBuffer::~VKFrameBuffer()
|
|
{
|
|
if (!immutable_) {
|
|
render_pass_free();
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
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])
|
|
{
|
|
return false;
|
|
}
|
|
|
|
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::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],
|
|
int /*channel_len*/,
|
|
int /*slot*/,
|
|
void * /*r_data*/)
|
|
{
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Blit operations
|
|
* \{ */
|
|
|
|
void VKFrameBuffer::blit_to(eGPUFrameBufferBits /*planes*/,
|
|
int /*src_slot*/,
|
|
FrameBuffer * /*dst*/,
|
|
int /*dst_slot*/,
|
|
int /*dst_offset_x*/,
|
|
int /*dst_offset_y*/)
|
|
{
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \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
|