Currently unused, but allows areas outside of DRW to render to the color render and depth texture. The primary user of this new API will be Sequencer preview to draw HDR images. Ref #138094 Pull Request: https://projects.blender.org/blender/blender/pulls/138306
635 lines
22 KiB
C++
635 lines
22 KiB
C++
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*
|
|
* System that manages viewport drawing.
|
|
*/
|
|
|
|
#include <cstring>
|
|
|
|
#include "BLI_math_vector.h"
|
|
#include "BLI_math_vector_types.hh"
|
|
#include "BLI_rect.h"
|
|
|
|
#include "BKE_colortools.hh"
|
|
|
|
#include "IMB_colormanagement.hh"
|
|
|
|
#include "DNA_vec_types.h"
|
|
|
|
#include "GPU_capabilities.hh"
|
|
#include "GPU_framebuffer.hh"
|
|
#include "GPU_immediate.hh"
|
|
#include "GPU_matrix.hh"
|
|
#include "GPU_state.hh"
|
|
#include "GPU_texture.hh"
|
|
#include "GPU_viewport.hh"
|
|
|
|
#include "DRW_engine.hh"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
/* Struct storing a viewport specific blender::gpu::Batch.
|
|
* The end-goal is to have a single batch shared across viewport and use a model matrix to place
|
|
* the batch. Due to OCIO and Image/UV editor we are not able to use an model matrix yet. */
|
|
struct GPUViewportBatch {
|
|
blender::gpu::Batch *batch;
|
|
struct {
|
|
rctf rect_pos;
|
|
rctf rect_uv;
|
|
} last_used_parameters;
|
|
};
|
|
|
|
static struct {
|
|
GPUVertFormat format;
|
|
struct {
|
|
uint pos, tex_coord;
|
|
} attr_id;
|
|
} g_viewport = {{0}};
|
|
|
|
struct GPUViewport {
|
|
blender::int2 size;
|
|
int flag;
|
|
|
|
/* Set the active view (for stereoscopic viewport rendering). */
|
|
int active_view;
|
|
|
|
/* Viewport Resources. */
|
|
DRWData *draw_data;
|
|
/** Color buffers, one for each stereo view. Only one if not stereo viewport. */
|
|
GPUTexture *color_render_tx[2];
|
|
GPUTexture *color_overlay_tx[2];
|
|
/** Depth buffer. Can be shared with GPUOffscreen. */
|
|
GPUTexture *depth_tx;
|
|
/** Compositing framebuffer for stereo viewport. */
|
|
GPUFrameBuffer *stereo_comp_fb;
|
|
/** Color render and overlay frame-buffers for drawing outside of DRW module.
|
|
* The render framebuffer is expected to be in the linear space and viewport will perform color
|
|
* management on it to bring it to the display space.
|
|
* The overlay frame-buffer is expected to be in the display space and viewport does not do any
|
|
* color management on it. */
|
|
GPUFrameBuffer *render_fb;
|
|
GPUFrameBuffer *overlay_fb;
|
|
|
|
/* Color management. */
|
|
ColorManagedViewSettings view_settings;
|
|
ColorManagedDisplaySettings display_settings;
|
|
CurveMapping *orig_curve_mapping;
|
|
float dither;
|
|
/* TODO(@fclem): the UV-image display use the viewport but do not set any view transform for the
|
|
* moment. The end goal would be to let the GPUViewport do the color management. */
|
|
bool do_color_management;
|
|
GPUViewportBatch batch;
|
|
};
|
|
|
|
enum {
|
|
DO_UPDATE = (1 << 0),
|
|
GPU_VIEWPORT_STEREO = (1 << 1),
|
|
};
|
|
|
|
void GPU_viewport_tag_update(GPUViewport *viewport)
|
|
{
|
|
viewport->flag |= DO_UPDATE;
|
|
}
|
|
|
|
bool GPU_viewport_do_update(GPUViewport *viewport)
|
|
{
|
|
bool ret = (viewport->flag & DO_UPDATE);
|
|
viewport->flag &= ~DO_UPDATE;
|
|
return ret;
|
|
}
|
|
|
|
GPUViewport *GPU_viewport_create()
|
|
{
|
|
GPUViewport *viewport = MEM_callocN<GPUViewport>("GPUViewport");
|
|
viewport->do_color_management = false;
|
|
viewport->size[0] = viewport->size[1] = -1;
|
|
viewport->active_view = 0;
|
|
return viewport;
|
|
}
|
|
|
|
GPUViewport *GPU_viewport_stereo_create()
|
|
{
|
|
GPUViewport *viewport = GPU_viewport_create();
|
|
viewport->flag = GPU_VIEWPORT_STEREO;
|
|
return viewport;
|
|
}
|
|
|
|
DRWData **GPU_viewport_data_get(GPUViewport *viewport)
|
|
{
|
|
return &viewport->draw_data;
|
|
}
|
|
|
|
static void gpu_viewport_textures_create(GPUViewport *viewport)
|
|
{
|
|
int *size = viewport->size;
|
|
float const empty_pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
|
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
|
|
|
|
if (viewport->color_render_tx[0] == nullptr) {
|
|
|
|
/* NOTE: dtxl_color texture requires write support as it may be written to by the viewport
|
|
* compositor. */
|
|
viewport->color_render_tx[0] = GPU_texture_create_2d("dtxl_color",
|
|
UNPACK2(size),
|
|
1,
|
|
GPU_RGBA16F,
|
|
usage | GPU_TEXTURE_USAGE_SHADER_WRITE,
|
|
nullptr);
|
|
viewport->color_overlay_tx[0] = GPU_texture_create_2d(
|
|
"dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, usage, nullptr);
|
|
|
|
GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel);
|
|
GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel);
|
|
}
|
|
|
|
if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0 && viewport->color_render_tx[1] == nullptr) {
|
|
viewport->color_render_tx[1] = GPU_texture_create_2d("dtxl_color_stereo",
|
|
UNPACK2(size),
|
|
1,
|
|
GPU_RGBA16F,
|
|
usage | GPU_TEXTURE_USAGE_SHADER_WRITE,
|
|
nullptr);
|
|
viewport->color_overlay_tx[1] = GPU_texture_create_2d(
|
|
"dtxl_color_overlay_stereo", UNPACK2(size), 1, GPU_SRGB8_A8, usage, nullptr);
|
|
|
|
GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel);
|
|
GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel);
|
|
}
|
|
|
|
/* Can be shared with #GPUOffscreen. */
|
|
if (viewport->depth_tx == nullptr) {
|
|
/* Depth texture can be read back by gizmos #view3d_depths_create. */
|
|
/* Swizzle flag is needed by Workbench Volumes to read the stencil view. */
|
|
viewport->depth_tx = GPU_texture_create_2d("dtxl_depth",
|
|
UNPACK2(size),
|
|
1,
|
|
GPU_DEPTH24_STENCIL8,
|
|
usage | GPU_TEXTURE_USAGE_HOST_READ |
|
|
GPU_TEXTURE_USAGE_FORMAT_VIEW,
|
|
nullptr);
|
|
const int depth_clear = 0;
|
|
GPU_texture_clear(viewport->depth_tx, GPU_DATA_UINT_24_8, &depth_clear);
|
|
}
|
|
|
|
if (!viewport->depth_tx || !viewport->color_render_tx[0] || !viewport->color_overlay_tx[0]) {
|
|
GPU_viewport_free(viewport);
|
|
}
|
|
}
|
|
|
|
static void gpu_viewport_textures_free(GPUViewport *viewport)
|
|
{
|
|
GPU_FRAMEBUFFER_FREE_SAFE(viewport->stereo_comp_fb);
|
|
GPU_FRAMEBUFFER_FREE_SAFE(viewport->render_fb);
|
|
GPU_FRAMEBUFFER_FREE_SAFE(viewport->overlay_fb);
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
GPU_TEXTURE_FREE_SAFE(viewport->color_render_tx[i]);
|
|
GPU_TEXTURE_FREE_SAFE(viewport->color_overlay_tx[i]);
|
|
}
|
|
|
|
GPU_TEXTURE_FREE_SAFE(viewport->depth_tx);
|
|
}
|
|
|
|
void GPU_viewport_bind(GPUViewport *viewport, int view, const rcti *rect)
|
|
{
|
|
blender::int2 rect_size;
|
|
/* add one pixel because of scissor test */
|
|
rect_size[0] = BLI_rcti_size_x(rect) + 1;
|
|
rect_size[1] = BLI_rcti_size_y(rect) + 1;
|
|
|
|
DRW_gpu_context_enable();
|
|
|
|
if (viewport->size != rect_size) {
|
|
copy_v2_v2_int(viewport->size, rect_size);
|
|
gpu_viewport_textures_free(viewport);
|
|
gpu_viewport_textures_create(viewport);
|
|
}
|
|
|
|
viewport->active_view = view;
|
|
}
|
|
|
|
void GPU_viewport_bind_from_offscreen(GPUViewport *viewport, GPUOffScreen *ofs, bool is_xr_surface)
|
|
{
|
|
GPUTexture *color, *depth;
|
|
GPUFrameBuffer *fb;
|
|
viewport->size[0] = GPU_offscreen_width(ofs);
|
|
viewport->size[1] = GPU_offscreen_height(ofs);
|
|
|
|
GPU_offscreen_viewport_data_get(ofs, &fb, &color, &depth);
|
|
|
|
/* XR surfaces will already check for texture size changes and free if necessary (see
|
|
* #wm_xr_session_surface_offscreen_ensure()), so don't free here as it has a significant
|
|
* performance impact (leads to texture re-creation in #gpu_viewport_textures_create() every VR
|
|
* drawing iteration). */
|
|
if (!is_xr_surface) {
|
|
gpu_viewport_textures_free(viewport);
|
|
}
|
|
|
|
/* This is the only texture we can share. */
|
|
viewport->depth_tx = depth;
|
|
|
|
gpu_viewport_textures_create(viewport);
|
|
}
|
|
|
|
void GPU_viewport_colorspace_set(GPUViewport *viewport,
|
|
const ColorManagedViewSettings *view_settings,
|
|
const ColorManagedDisplaySettings *display_settings,
|
|
float dither)
|
|
{
|
|
/**
|
|
* HACK(fclem): We copy the settings here to avoid use after free if an update frees the scene
|
|
* and the viewport stays cached (see #75443). But this means the OCIO curve-mapping caching
|
|
* (which is based on #CurveMap pointer address) cannot operate correctly and it will create
|
|
* a different OCIO processor for each viewport. We try to only reallocate the curve-map copy
|
|
* if needed to avoid unneeded cache invalidation.
|
|
*/
|
|
if (view_settings->curve_mapping) {
|
|
if (viewport->view_settings.curve_mapping) {
|
|
if (view_settings->curve_mapping->changed_timestamp !=
|
|
viewport->view_settings.curve_mapping->changed_timestamp)
|
|
{
|
|
BKE_color_managed_view_settings_free(&viewport->view_settings);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (viewport->orig_curve_mapping != view_settings->curve_mapping) {
|
|
viewport->orig_curve_mapping = view_settings->curve_mapping;
|
|
BKE_color_managed_view_settings_free(&viewport->view_settings);
|
|
}
|
|
/* Don't copy the curve mapping already. */
|
|
BKE_color_managed_view_settings_copy_keep_curve_mapping(&viewport->view_settings, view_settings);
|
|
/* Only copy curve-mapping if needed. Avoid unneeded OCIO cache miss. */
|
|
if (view_settings->curve_mapping && viewport->view_settings.curve_mapping == nullptr) {
|
|
BKE_color_managed_view_settings_free(&viewport->view_settings);
|
|
viewport->view_settings.curve_mapping = BKE_curvemapping_copy(view_settings->curve_mapping);
|
|
}
|
|
|
|
BKE_color_managed_display_settings_copy(&viewport->display_settings, display_settings);
|
|
viewport->dither = dither;
|
|
viewport->do_color_management = true;
|
|
}
|
|
|
|
void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo_format)
|
|
{
|
|
if (!ELEM(stereo_format->display_mode, S3D_DISPLAY_ANAGLYPH, S3D_DISPLAY_INTERLACE)) {
|
|
/* Early Exit: the other display modes need access to the full screen and cannot be
|
|
* done from a single viewport. See `wm_stereo.cc`. */
|
|
return;
|
|
}
|
|
/* The composite framebuffer object needs to be created in the window context. */
|
|
GPU_framebuffer_ensure_config(
|
|
&viewport->stereo_comp_fb,
|
|
{
|
|
GPU_ATTACHMENT_NONE,
|
|
/* We need the sRGB attachment to be first for GL_FRAMEBUFFER_SRGB to be turned on.
|
|
* Note that this is the opposite of what the texture binding is. */
|
|
GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]),
|
|
GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[0]),
|
|
});
|
|
|
|
GPUVertFormat *vert_format = immVertexFormat();
|
|
uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
GPU_framebuffer_bind(viewport->stereo_comp_fb);
|
|
GPU_matrix_push();
|
|
GPU_matrix_push_projection();
|
|
GPU_matrix_identity_set();
|
|
GPU_matrix_identity_projection_set();
|
|
immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE);
|
|
int settings = stereo_format->display_mode;
|
|
if (settings == S3D_DISPLAY_ANAGLYPH) {
|
|
switch (stereo_format->anaglyph_type) {
|
|
case S3D_ANAGLYPH_REDCYAN:
|
|
GPU_color_mask(false, true, true, true);
|
|
break;
|
|
case S3D_ANAGLYPH_GREENMAGENTA:
|
|
GPU_color_mask(true, false, true, true);
|
|
break;
|
|
case S3D_ANAGLYPH_YELLOWBLUE:
|
|
GPU_color_mask(false, false, true, true);
|
|
break;
|
|
}
|
|
}
|
|
else if (settings == S3D_DISPLAY_INTERLACE) {
|
|
settings |= stereo_format->interlace_type << 3;
|
|
SET_FLAG_FROM_TEST(settings, stereo_format->flag & S3D_INTERLACE_SWAP, 1 << 6);
|
|
}
|
|
immUniform1i("stereoDisplaySettings", settings);
|
|
|
|
GPU_texture_bind(viewport->color_render_tx[1], 0);
|
|
GPU_texture_bind(viewport->color_overlay_tx[1], 1);
|
|
|
|
immBegin(GPU_PRIM_TRI_STRIP, 4);
|
|
|
|
immVertex2f(pos, -1.0f, -1.0f);
|
|
immVertex2f(pos, 1.0f, -1.0f);
|
|
immVertex2f(pos, -1.0f, 1.0f);
|
|
immVertex2f(pos, 1.0f, 1.0f);
|
|
|
|
immEnd();
|
|
|
|
GPU_texture_unbind(viewport->color_render_tx[1]);
|
|
GPU_texture_unbind(viewport->color_overlay_tx[1]);
|
|
|
|
immUnbindProgram();
|
|
GPU_matrix_pop_projection();
|
|
GPU_matrix_pop();
|
|
|
|
if (settings == S3D_DISPLAY_ANAGLYPH) {
|
|
GPU_color_mask(true, true, true, true);
|
|
}
|
|
|
|
GPU_framebuffer_restore();
|
|
}
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Viewport Batches
|
|
* \{ */
|
|
|
|
static const GPUVertFormat &gpu_viewport_batch_format()
|
|
{
|
|
if (g_viewport.format.attr_len == 0) {
|
|
GPUVertFormat *format = &g_viewport.format;
|
|
g_viewport.attr_id.pos = GPU_vertformat_attr_add(
|
|
format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
g_viewport.attr_id.tex_coord = GPU_vertformat_attr_add(
|
|
format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
}
|
|
return g_viewport.format;
|
|
}
|
|
|
|
static blender::gpu::Batch *gpu_viewport_batch_create(const rctf *rect_pos, const rctf *rect_uv)
|
|
{
|
|
blender::gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(gpu_viewport_batch_format());
|
|
const uint vbo_len = 4;
|
|
GPU_vertbuf_data_alloc(*vbo, vbo_len);
|
|
|
|
GPUVertBufRaw pos_step, tex_coord_step;
|
|
GPU_vertbuf_attr_get_raw_data(vbo, g_viewport.attr_id.pos, &pos_step);
|
|
GPU_vertbuf_attr_get_raw_data(vbo, g_viewport.attr_id.tex_coord, &tex_coord_step);
|
|
|
|
copy_v2_fl2(
|
|
static_cast<float *>(GPU_vertbuf_raw_step(&pos_step)), rect_pos->xmin, rect_pos->ymin);
|
|
copy_v2_fl2(
|
|
static_cast<float *>(GPU_vertbuf_raw_step(&tex_coord_step)), rect_uv->xmin, rect_uv->ymin);
|
|
copy_v2_fl2(
|
|
static_cast<float *>(GPU_vertbuf_raw_step(&pos_step)), rect_pos->xmax, rect_pos->ymin);
|
|
copy_v2_fl2(
|
|
static_cast<float *>(GPU_vertbuf_raw_step(&tex_coord_step)), rect_uv->xmax, rect_uv->ymin);
|
|
copy_v2_fl2(
|
|
static_cast<float *>(GPU_vertbuf_raw_step(&pos_step)), rect_pos->xmin, rect_pos->ymax);
|
|
copy_v2_fl2(
|
|
static_cast<float *>(GPU_vertbuf_raw_step(&tex_coord_step)), rect_uv->xmin, rect_uv->ymax);
|
|
copy_v2_fl2(
|
|
static_cast<float *>(GPU_vertbuf_raw_step(&pos_step)), rect_pos->xmax, rect_pos->ymax);
|
|
copy_v2_fl2(
|
|
static_cast<float *>(GPU_vertbuf_raw_step(&tex_coord_step)), rect_uv->xmax, rect_uv->ymax);
|
|
|
|
return GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
|
}
|
|
|
|
static blender::gpu::Batch *gpu_viewport_batch_get(GPUViewport *viewport,
|
|
const rctf *rect_pos,
|
|
const rctf *rect_uv)
|
|
{
|
|
const float compare_limit = 0.0001f;
|
|
const bool parameters_changed =
|
|
(!BLI_rctf_compare(
|
|
&viewport->batch.last_used_parameters.rect_pos, rect_pos, compare_limit) ||
|
|
!BLI_rctf_compare(&viewport->batch.last_used_parameters.rect_uv, rect_uv, compare_limit));
|
|
|
|
if (viewport->batch.batch && parameters_changed) {
|
|
GPU_batch_discard(viewport->batch.batch);
|
|
viewport->batch.batch = nullptr;
|
|
}
|
|
|
|
if (!viewport->batch.batch) {
|
|
viewport->batch.batch = gpu_viewport_batch_create(rect_pos, rect_uv);
|
|
viewport->batch.last_used_parameters.rect_pos = *rect_pos;
|
|
viewport->batch.last_used_parameters.rect_uv = *rect_uv;
|
|
}
|
|
return viewport->batch.batch;
|
|
}
|
|
|
|
static void gpu_viewport_batch_free(GPUViewport *viewport)
|
|
{
|
|
if (viewport->batch.batch) {
|
|
GPU_batch_discard(viewport->batch.batch);
|
|
viewport->batch.batch = nullptr;
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
static void gpu_viewport_draw_colormanaged(GPUViewport *viewport,
|
|
int view,
|
|
const rctf *rect_pos,
|
|
const rctf *rect_uv,
|
|
bool display_colorspace,
|
|
bool do_overlay_merge)
|
|
{
|
|
GPUTexture *color = viewport->color_render_tx[view];
|
|
GPUTexture *color_overlay = viewport->color_overlay_tx[view];
|
|
|
|
bool use_ocio = false;
|
|
bool use_hdr = GPU_hdr_support() &&
|
|
((viewport->view_settings.flag & COLORMANAGE_VIEW_USE_HDR) != 0);
|
|
|
|
if (viewport->do_color_management && display_colorspace) {
|
|
/* During the binding process the last used VertexFormat is tested and can assert as it is not
|
|
* valid. By calling the `immVertexFormat` the last used VertexFormat is reset and the assert
|
|
* does not happen. This solves a chicken and egg problem when using GPUBatches. GPUBatches
|
|
* contain the correct vertex format, but can only bind after the shader is bound.
|
|
*
|
|
* Image/UV editor still uses imm, after that has been changed we could move this fix to the
|
|
* OCIO. */
|
|
immVertexFormat();
|
|
use_ocio = IMB_colormanagement_setup_glsl_draw_from_space(&viewport->view_settings,
|
|
&viewport->display_settings,
|
|
nullptr,
|
|
viewport->dither,
|
|
false,
|
|
do_overlay_merge);
|
|
}
|
|
|
|
blender::gpu::Batch *batch = gpu_viewport_batch_get(viewport, rect_pos, rect_uv);
|
|
if (use_ocio) {
|
|
GPU_batch_program_set_imm_shader(batch);
|
|
}
|
|
else {
|
|
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE);
|
|
GPU_batch_uniform_1i(batch, "overlay", do_overlay_merge);
|
|
GPU_batch_uniform_1i(batch, "display_transform", display_colorspace);
|
|
GPU_batch_uniform_1i(batch, "use_hdr", use_hdr);
|
|
}
|
|
|
|
GPU_texture_bind(color, 0);
|
|
GPU_texture_bind(color_overlay, 1);
|
|
GPU_batch_draw(batch);
|
|
GPU_texture_unbind(color);
|
|
GPU_texture_unbind(color_overlay);
|
|
|
|
if (use_ocio) {
|
|
IMB_colormanagement_finish_glsl_draw();
|
|
}
|
|
}
|
|
|
|
void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport,
|
|
int view,
|
|
const rcti *rect,
|
|
bool display_colorspace,
|
|
bool do_overlay_merge)
|
|
{
|
|
GPUTexture *color = viewport->color_render_tx[view];
|
|
|
|
if (color == nullptr) {
|
|
return;
|
|
}
|
|
|
|
const float w = float(GPU_texture_width(color));
|
|
const float h = float(GPU_texture_height(color));
|
|
|
|
/* We allow rects with min/max swapped, but we also need correctly assigned coordinates. */
|
|
rcti sanitized_rect = *rect;
|
|
BLI_rcti_sanitize(&sanitized_rect);
|
|
|
|
BLI_assert(w == BLI_rcti_size_x(&sanitized_rect) + 1);
|
|
BLI_assert(h == BLI_rcti_size_y(&sanitized_rect) + 1);
|
|
|
|
/* wmOrtho for the screen has this same offset */
|
|
const float halfx = GLA_PIXEL_OFS / w;
|
|
const float halfy = GLA_PIXEL_OFS / h;
|
|
|
|
rctf pos_rect{};
|
|
pos_rect.xmin = sanitized_rect.xmin;
|
|
pos_rect.ymin = sanitized_rect.ymin;
|
|
pos_rect.xmax = sanitized_rect.xmin + w;
|
|
pos_rect.ymax = sanitized_rect.ymin + h;
|
|
|
|
rctf uv_rect{};
|
|
uv_rect.xmin = halfx;
|
|
uv_rect.ymin = halfy;
|
|
uv_rect.xmax = halfx + 1.0f;
|
|
uv_rect.ymax = halfy + 1.0f;
|
|
|
|
/* Mirror the UV rect in case axis-swapped drawing is requested (by passing a rect with min and
|
|
* max values swapped). */
|
|
if (BLI_rcti_size_x(rect) < 0) {
|
|
std::swap(uv_rect.xmin, uv_rect.xmax);
|
|
}
|
|
if (BLI_rcti_size_y(rect) < 0) {
|
|
std::swap(uv_rect.ymin, uv_rect.ymax);
|
|
}
|
|
|
|
gpu_viewport_draw_colormanaged(
|
|
viewport, view, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge);
|
|
}
|
|
|
|
void GPU_viewport_draw_to_screen(GPUViewport *viewport, int view, const rcti *rect)
|
|
{
|
|
GPU_viewport_draw_to_screen_ex(viewport, view, rect, true, true);
|
|
}
|
|
|
|
void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport,
|
|
GPUOffScreen *ofs,
|
|
bool display_colorspace,
|
|
bool do_overlay_merge)
|
|
{
|
|
const int view = 0;
|
|
|
|
if (viewport->color_render_tx[view] == nullptr) {
|
|
return;
|
|
}
|
|
|
|
GPU_depth_test(GPU_DEPTH_NONE);
|
|
GPU_offscreen_bind(ofs, false);
|
|
|
|
rctf pos_rect{};
|
|
pos_rect.xmin = -1.0f;
|
|
pos_rect.ymin = -1.0f;
|
|
pos_rect.xmax = 1.0f;
|
|
pos_rect.ymax = 1.0f;
|
|
|
|
rctf uv_rect{};
|
|
uv_rect.xmin = 0.0f;
|
|
uv_rect.ymin = 0.0f;
|
|
uv_rect.xmax = 1.0f;
|
|
uv_rect.ymax = 1.0f;
|
|
|
|
gpu_viewport_draw_colormanaged(
|
|
viewport, view, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge);
|
|
|
|
/* This one is from the offscreen. Don't free it with the viewport. */
|
|
viewport->depth_tx = nullptr;
|
|
}
|
|
|
|
void GPU_viewport_unbind(GPUViewport * /*viewport*/)
|
|
{
|
|
GPU_framebuffer_restore();
|
|
DRW_gpu_context_disable();
|
|
}
|
|
|
|
int GPU_viewport_active_view_get(GPUViewport *viewport)
|
|
{
|
|
return viewport->active_view;
|
|
}
|
|
|
|
bool GPU_viewport_is_stereo_get(GPUViewport *viewport)
|
|
{
|
|
return (viewport->flag & GPU_VIEWPORT_STEREO) != 0;
|
|
}
|
|
|
|
GPUTexture *GPU_viewport_color_texture(GPUViewport *viewport, int view)
|
|
{
|
|
return viewport->color_render_tx[view];
|
|
}
|
|
|
|
GPUTexture *GPU_viewport_overlay_texture(GPUViewport *viewport, int view)
|
|
{
|
|
return viewport->color_overlay_tx[view];
|
|
}
|
|
|
|
GPUTexture *GPU_viewport_depth_texture(GPUViewport *viewport)
|
|
{
|
|
return viewport->depth_tx;
|
|
}
|
|
|
|
GPUFrameBuffer *GPU_viewport_framebuffer_render_get(GPUViewport *viewport)
|
|
{
|
|
GPU_framebuffer_ensure_config(
|
|
&viewport->render_fb,
|
|
{
|
|
GPU_ATTACHMENT_TEXTURE(viewport->depth_tx),
|
|
GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[viewport->active_view]),
|
|
});
|
|
return viewport->render_fb;
|
|
}
|
|
|
|
GPUFrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport)
|
|
{
|
|
GPU_framebuffer_ensure_config(
|
|
&viewport->overlay_fb,
|
|
{
|
|
GPU_ATTACHMENT_TEXTURE(viewport->depth_tx),
|
|
GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[viewport->active_view]),
|
|
});
|
|
return viewport->overlay_fb;
|
|
}
|
|
|
|
void GPU_viewport_free(GPUViewport *viewport)
|
|
{
|
|
if (viewport->draw_data) {
|
|
DRW_viewport_data_free(viewport->draw_data);
|
|
}
|
|
|
|
gpu_viewport_textures_free(viewport);
|
|
|
|
BKE_color_managed_view_settings_free(&viewport->view_settings);
|
|
gpu_viewport_batch_free(viewport);
|
|
|
|
MEM_freeN(viewport);
|
|
}
|