Cycles: use GPU module for viewport display
To make GPU backends other than OpenGL work. Adds required pixel buffer and fence objects to GPU module. Authored by Apple: Michael Parkin-White Ref T96261 Ref T92212 Reviewed By: fclem, brecht Differential Revision: https://developer.blender.org/D16042
This commit is contained in:
committed by
Brecht Van Lommel
parent
b5ebc9bb24
commit
b132e3b3ce
@@ -5,9 +5,14 @@
|
||||
|
||||
#include "device/device.h"
|
||||
#include "util/log.h"
|
||||
#include "util/math.h"
|
||||
#include "util/opengl.h"
|
||||
|
||||
#include "GPU_platform.h"
|
||||
#include "GPU_context.h"
|
||||
#include "GPU_immediate.h"
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_state.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
@@ -30,8 +35,9 @@ unique_ptr<BlenderDisplayShader> BlenderDisplayShader::create(BL::RenderEngine &
|
||||
int BlenderDisplayShader::get_position_attrib_location()
|
||||
{
|
||||
if (position_attribute_location_ == -1) {
|
||||
const uint shader_program = get_shader_program();
|
||||
position_attribute_location_ = glGetAttribLocation(shader_program, position_attribute_name);
|
||||
GPUShader *shader_program = get_shader_program();
|
||||
position_attribute_location_ = GPU_shader_get_attribute(shader_program,
|
||||
position_attribute_name);
|
||||
}
|
||||
return position_attribute_location_;
|
||||
}
|
||||
@@ -39,8 +45,9 @@ int BlenderDisplayShader::get_position_attrib_location()
|
||||
int BlenderDisplayShader::get_tex_coord_attrib_location()
|
||||
{
|
||||
if (tex_coord_attribute_location_ == -1) {
|
||||
const uint shader_program = get_shader_program();
|
||||
tex_coord_attribute_location_ = glGetAttribLocation(shader_program, tex_coord_attribute_name);
|
||||
GPUShader *shader_program = get_shader_program();
|
||||
tex_coord_attribute_location_ = GPU_shader_get_attribute(shader_program,
|
||||
tex_coord_attribute_name);
|
||||
}
|
||||
return tex_coord_attribute_location_;
|
||||
}
|
||||
@@ -79,100 +86,42 @@ static const char *FALLBACK_FRAGMENT_SHADER =
|
||||
" fragColor = texture(image_texture, texCoord_interp);\n"
|
||||
"}\n\0";
|
||||
|
||||
static void shader_print_errors(const char *task, const char *log, const char *code)
|
||||
static GPUShader *compile_fallback_shader(void)
|
||||
{
|
||||
LOG(ERROR) << "Shader: " << task << " error:";
|
||||
LOG(ERROR) << "===== shader string ====";
|
||||
|
||||
stringstream stream(code);
|
||||
string partial;
|
||||
|
||||
int line = 1;
|
||||
while (getline(stream, partial, '\n')) {
|
||||
if (line < 10) {
|
||||
LOG(ERROR) << " " << line << " " << partial;
|
||||
}
|
||||
else {
|
||||
LOG(ERROR) << line << " " << partial;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
LOG(ERROR) << log;
|
||||
/* NOTE: Compilation errors are logged to console. */
|
||||
GPUShader *shader = GPU_shader_create(FALLBACK_VERTEX_SHADER,
|
||||
FALLBACK_FRAGMENT_SHADER,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"FallbackCyclesBlitShader");
|
||||
return shader;
|
||||
}
|
||||
|
||||
static int compile_fallback_shader(void)
|
||||
{
|
||||
const struct Shader {
|
||||
const char *source;
|
||||
const GLenum type;
|
||||
} shaders[2] = {{FALLBACK_VERTEX_SHADER, GL_VERTEX_SHADER},
|
||||
{FALLBACK_FRAGMENT_SHADER, GL_FRAGMENT_SHADER}};
|
||||
|
||||
const GLuint program = glCreateProgram();
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
const GLuint shader = glCreateShader(shaders[i].type);
|
||||
|
||||
string source_str = shaders[i].source;
|
||||
const char *c_str = source_str.c_str();
|
||||
|
||||
glShaderSource(shader, 1, &c_str, NULL);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint compile_status;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
|
||||
|
||||
if (!compile_status) {
|
||||
GLchar log[5000];
|
||||
GLsizei length = 0;
|
||||
glGetShaderInfoLog(shader, sizeof(log), &length, log);
|
||||
shader_print_errors("compile", log, c_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
glAttachShader(program, shader);
|
||||
}
|
||||
|
||||
/* Link output. */
|
||||
glBindFragDataLocation(program, 0, "fragColor");
|
||||
|
||||
/* Link and error check. */
|
||||
glLinkProgram(program);
|
||||
|
||||
/* TODO(sergey): Find a way to nicely de-duplicate the error checking. */
|
||||
GLint link_status;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &link_status);
|
||||
if (!link_status) {
|
||||
GLchar log[5000];
|
||||
GLsizei length = 0;
|
||||
/* TODO(sergey): Is it really program passed to glGetShaderInfoLog? */
|
||||
glGetShaderInfoLog(program, sizeof(log), &length, log);
|
||||
shader_print_errors("linking", log, FALLBACK_VERTEX_SHADER);
|
||||
shader_print_errors("linking", log, FALLBACK_FRAGMENT_SHADER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
void BlenderFallbackDisplayShader::bind(int width, int height)
|
||||
GPUShader *BlenderFallbackDisplayShader::bind(int width, int height)
|
||||
{
|
||||
create_shader_if_needed();
|
||||
|
||||
if (!shader_program_) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glUseProgram(shader_program_);
|
||||
glUniform1i(image_texture_location_, 0);
|
||||
glUniform2f(fullscreen_location_, width, height);
|
||||
/* Bind shader now to enable uniform assignment. */
|
||||
GPU_shader_bind(shader_program_);
|
||||
GPU_shader_uniform_int(shader_program_, image_texture_location_, 0);
|
||||
float size[2];
|
||||
size[0] = width;
|
||||
size[1] = height;
|
||||
GPU_shader_uniform_vector(shader_program_, fullscreen_location_, 2, 1, size);
|
||||
return shader_program_;
|
||||
}
|
||||
|
||||
void BlenderFallbackDisplayShader::unbind()
|
||||
{
|
||||
GPU_shader_unbind();
|
||||
}
|
||||
|
||||
uint BlenderFallbackDisplayShader::get_shader_program()
|
||||
GPUShader *BlenderFallbackDisplayShader::get_shader_program()
|
||||
{
|
||||
return shader_program_;
|
||||
}
|
||||
@@ -187,19 +136,18 @@ void BlenderFallbackDisplayShader::create_shader_if_needed()
|
||||
|
||||
shader_program_ = compile_fallback_shader();
|
||||
if (!shader_program_) {
|
||||
LOG(ERROR) << "Failed to compile fallback shader";
|
||||
return;
|
||||
}
|
||||
|
||||
glUseProgram(shader_program_);
|
||||
|
||||
image_texture_location_ = glGetUniformLocation(shader_program_, "image_texture");
|
||||
image_texture_location_ = GPU_shader_get_uniform(shader_program_, "image_texture");
|
||||
if (image_texture_location_ < 0) {
|
||||
LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform.";
|
||||
destroy_shader();
|
||||
return;
|
||||
}
|
||||
|
||||
fullscreen_location_ = glGetUniformLocation(shader_program_, "fullscreen");
|
||||
fullscreen_location_ = GPU_shader_get_uniform(shader_program_, "fullscreen");
|
||||
if (fullscreen_location_ < 0) {
|
||||
LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform.";
|
||||
destroy_shader();
|
||||
@@ -209,8 +157,10 @@ void BlenderFallbackDisplayShader::create_shader_if_needed()
|
||||
|
||||
void BlenderFallbackDisplayShader::destroy_shader()
|
||||
{
|
||||
glDeleteProgram(shader_program_);
|
||||
shader_program_ = 0;
|
||||
if (shader_program_) {
|
||||
GPU_shader_free(shader_program_);
|
||||
shader_program_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
@@ -224,9 +174,10 @@ BlenderDisplaySpaceShader::BlenderDisplaySpaceShader(BL::RenderEngine &b_engine,
|
||||
DCHECK(b_engine_.support_display_space_shader(b_scene_));
|
||||
}
|
||||
|
||||
void BlenderDisplaySpaceShader::bind(int /*width*/, int /*height*/)
|
||||
GPUShader *BlenderDisplaySpaceShader::bind(int /*width*/, int /*height*/)
|
||||
{
|
||||
b_engine_.bind_display_space_shader(b_scene_);
|
||||
return GPU_shader_get_bound();
|
||||
}
|
||||
|
||||
void BlenderDisplaySpaceShader::unbind()
|
||||
@@ -234,12 +185,11 @@ void BlenderDisplaySpaceShader::unbind()
|
||||
b_engine_.unbind_display_space_shader();
|
||||
}
|
||||
|
||||
uint BlenderDisplaySpaceShader::get_shader_program()
|
||||
GPUShader *BlenderDisplaySpaceShader::get_shader_program()
|
||||
{
|
||||
if (!shader_program_) {
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, reinterpret_cast<int *>(&shader_program_));
|
||||
shader_program_ = GPU_shader_get_bound();
|
||||
}
|
||||
|
||||
if (!shader_program_) {
|
||||
LOG(ERROR) << "Error retrieving shader program for display space shader.";
|
||||
}
|
||||
@@ -252,34 +202,34 @@ uint BlenderDisplaySpaceShader::get_shader_program()
|
||||
*/
|
||||
|
||||
/* Higher level representation of a texture from the graphics library. */
|
||||
class GLTexture {
|
||||
class DisplayGPUTexture {
|
||||
public:
|
||||
/* Global counter for all allocated OpenGL textures used by instances of this class. */
|
||||
/* Global counter for all allocated GPUTextures used by instances of this class. */
|
||||
static inline std::atomic<int> num_used = 0;
|
||||
|
||||
GLTexture() = default;
|
||||
DisplayGPUTexture() = default;
|
||||
|
||||
~GLTexture()
|
||||
~DisplayGPUTexture()
|
||||
{
|
||||
assert(gl_id == 0);
|
||||
assert(gpu_texture == nullptr);
|
||||
}
|
||||
|
||||
GLTexture(const GLTexture &other) = delete;
|
||||
GLTexture &operator=(GLTexture &other) = delete;
|
||||
DisplayGPUTexture(const DisplayGPUTexture &other) = delete;
|
||||
DisplayGPUTexture &operator=(DisplayGPUTexture &other) = delete;
|
||||
|
||||
GLTexture(GLTexture &&other) noexcept
|
||||
: gl_id(other.gl_id), width(other.width), height(other.height)
|
||||
DisplayGPUTexture(DisplayGPUTexture &&other) noexcept
|
||||
: gpu_texture(other.gpu_texture), width(other.width), height(other.height)
|
||||
{
|
||||
other.reset();
|
||||
}
|
||||
|
||||
GLTexture &operator=(GLTexture &&other)
|
||||
DisplayGPUTexture &operator=(DisplayGPUTexture &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
gl_id = other.gl_id;
|
||||
gpu_texture = other.gpu_texture;
|
||||
width = other.width;
|
||||
height = other.height;
|
||||
|
||||
@@ -288,55 +238,56 @@ class GLTexture {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool gl_resources_ensure()
|
||||
bool gpu_resources_ensure()
|
||||
{
|
||||
if (gl_id) {
|
||||
if (gpu_texture) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Create texture. */
|
||||
glGenTextures(1, &gl_id);
|
||||
if (!gl_id) {
|
||||
/* Texture must have a minimum size of 1x1. */
|
||||
gpu_texture = GPU_texture_create_2d(
|
||||
"CyclesBlitTexture", max(width, 1), max(height, 1), 1, GPU_RGBA16F, nullptr);
|
||||
|
||||
if (!gpu_texture) {
|
||||
LOG(ERROR) << "Error creating texture.";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Configure the texture. */
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_id);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
/* Clamp to edge so that precision issues when zoomed out (which forces linear interpolation)
|
||||
* does not cause unwanted repetition. */
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
GPU_texture_filter_mode(gpu_texture, false);
|
||||
GPU_texture_wrap_mode(gpu_texture, false, true);
|
||||
|
||||
++num_used;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gl_resources_destroy()
|
||||
void gpu_resources_destroy()
|
||||
{
|
||||
if (!gl_id) {
|
||||
if (gpu_texture == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
glDeleteTextures(1, &gl_id);
|
||||
GPU_TEXTURE_FREE_SAFE(gpu_texture);
|
||||
|
||||
reset();
|
||||
|
||||
--num_used;
|
||||
}
|
||||
|
||||
/* OpenGL resource IDs of the texture.
|
||||
void ensure_size(uint texture_width, uint texture_height)
|
||||
{
|
||||
if (width != texture_width || height != texture_height) {
|
||||
gpu_resources_destroy();
|
||||
width = texture_width;
|
||||
height = texture_height;
|
||||
gpu_resources_ensure();
|
||||
}
|
||||
}
|
||||
|
||||
/* Texture resource allocated by the GPU module.
|
||||
*
|
||||
* NOTE: Allocated on the render engine's context. */
|
||||
uint gl_id = 0;
|
||||
GPUTexture *gpu_texture = nullptr;
|
||||
|
||||
/* Dimensions of the texture in pixels. */
|
||||
int width = 0;
|
||||
@@ -345,41 +296,41 @@ class GLTexture {
|
||||
protected:
|
||||
void reset()
|
||||
{
|
||||
gl_id = 0;
|
||||
gpu_texture = nullptr;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/* Higher level representation of a Pixel Buffer Object (PBO) from the graphics library. */
|
||||
class GLPixelBufferObject {
|
||||
class DisplayGPUPixelBuffer {
|
||||
public:
|
||||
/* Global counter for all allocated OpenGL PBOs used by instances of this class. */
|
||||
/* Global counter for all allocated GPU module PBOs used by instances of this class. */
|
||||
static inline std::atomic<int> num_used = 0;
|
||||
|
||||
GLPixelBufferObject() = default;
|
||||
DisplayGPUPixelBuffer() = default;
|
||||
|
||||
~GLPixelBufferObject()
|
||||
~DisplayGPUPixelBuffer()
|
||||
{
|
||||
assert(gl_id == 0);
|
||||
assert(gpu_pixel_buffer == nullptr);
|
||||
}
|
||||
|
||||
GLPixelBufferObject(const GLPixelBufferObject &other) = delete;
|
||||
GLPixelBufferObject &operator=(GLPixelBufferObject &other) = delete;
|
||||
DisplayGPUPixelBuffer(const DisplayGPUPixelBuffer &other) = delete;
|
||||
DisplayGPUPixelBuffer &operator=(DisplayGPUPixelBuffer &other) = delete;
|
||||
|
||||
GLPixelBufferObject(GLPixelBufferObject &&other) noexcept
|
||||
: gl_id(other.gl_id), width(other.width), height(other.height)
|
||||
DisplayGPUPixelBuffer(DisplayGPUPixelBuffer &&other) noexcept
|
||||
: gpu_pixel_buffer(other.gpu_pixel_buffer), width(other.width), height(other.height)
|
||||
{
|
||||
other.reset();
|
||||
}
|
||||
|
||||
GLPixelBufferObject &operator=(GLPixelBufferObject &&other)
|
||||
DisplayGPUPixelBuffer &operator=(DisplayGPUPixelBuffer &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
gl_id = other.gl_id;
|
||||
gpu_pixel_buffer = other.gpu_pixel_buffer;
|
||||
width = other.width;
|
||||
height = other.height;
|
||||
|
||||
@@ -388,14 +339,34 @@ class GLPixelBufferObject {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool gl_resources_ensure()
|
||||
void ensure_size(uint new_width, uint new_height)
|
||||
{
|
||||
if (gl_id) {
|
||||
return true;
|
||||
size_t required_size = sizeof(half4) * new_width * new_height * 4;
|
||||
|
||||
if (gpu_pixel_buffer) {
|
||||
if (new_width != width || new_height != height ||
|
||||
GPU_pixel_buffer_size(gpu_pixel_buffer) < required_size) {
|
||||
GPU_pixel_buffer_free(gpu_pixel_buffer);
|
||||
gpu_pixel_buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
glGenBuffers(1, &gl_id);
|
||||
if (!gl_id) {
|
||||
/* Update size. */
|
||||
width = new_width;
|
||||
height = new_height;
|
||||
|
||||
/* Create pixel buffer if not already created. */
|
||||
if (!gpu_pixel_buffer) {
|
||||
gpu_pixel_buffer = GPU_pixel_buffer_create(required_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool gpu_resources_ensure()
|
||||
{
|
||||
/* Create pixel buffer using current size. */
|
||||
ensure_size(width, height);
|
||||
|
||||
if (gpu_pixel_buffer == nullptr) {
|
||||
LOG(ERROR) << "Error creating texture pixel buffer object.";
|
||||
return false;
|
||||
}
|
||||
@@ -405,23 +376,24 @@ class GLPixelBufferObject {
|
||||
return true;
|
||||
}
|
||||
|
||||
void gl_resources_destroy()
|
||||
void gpu_resources_destroy()
|
||||
{
|
||||
if (!gl_id) {
|
||||
if (!gpu_pixel_buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
glDeleteBuffers(1, &gl_id);
|
||||
GPU_pixel_buffer_free(gpu_pixel_buffer);
|
||||
gpu_pixel_buffer = nullptr;
|
||||
|
||||
reset();
|
||||
|
||||
--num_used;
|
||||
}
|
||||
|
||||
/* OpenGL resource IDs of the PBO.
|
||||
/* Pixel Buffer Object allocated by the GPU module.
|
||||
*
|
||||
* NOTE: Allocated on the render engine's context. */
|
||||
uint gl_id = 0;
|
||||
GPUPixelBuffer *gpu_pixel_buffer = nullptr;
|
||||
|
||||
/* Dimensions of the PBO. */
|
||||
int width = 0;
|
||||
@@ -430,7 +402,7 @@ class GLPixelBufferObject {
|
||||
protected:
|
||||
void reset()
|
||||
{
|
||||
gl_id = 0;
|
||||
gpu_pixel_buffer = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
@@ -448,28 +420,28 @@ class DrawTile {
|
||||
|
||||
DrawTile &operator=(DrawTile &&other) = default;
|
||||
|
||||
bool gl_resources_ensure()
|
||||
bool gpu_resources_ensure()
|
||||
{
|
||||
if (!texture.gl_resources_ensure()) {
|
||||
gl_resources_destroy();
|
||||
if (!texture.gpu_resources_ensure()) {
|
||||
gpu_resources_destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gl_resources_destroy()
|
||||
void gpu_resources_destroy()
|
||||
{
|
||||
texture.gl_resources_destroy();
|
||||
texture.gpu_resources_destroy();
|
||||
}
|
||||
|
||||
inline bool ready_to_draw() const
|
||||
{
|
||||
return texture.gl_id != 0;
|
||||
return texture.gpu_texture != 0;
|
||||
}
|
||||
|
||||
/* Texture which contains pixels of the tile. */
|
||||
GLTexture texture;
|
||||
DisplayGPUTexture texture;
|
||||
|
||||
/* Display parameters the texture of this tile has been updated for. */
|
||||
BlenderDisplayDriver::Params params;
|
||||
@@ -477,24 +449,24 @@ class DrawTile {
|
||||
|
||||
class DrawTileAndPBO {
|
||||
public:
|
||||
bool gl_resources_ensure()
|
||||
bool gpu_resources_ensure()
|
||||
{
|
||||
if (!tile.gl_resources_ensure() || !buffer_object.gl_resources_ensure()) {
|
||||
gl_resources_destroy();
|
||||
if (!tile.gpu_resources_ensure() || !buffer_object.gpu_resources_ensure()) {
|
||||
gpu_resources_destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gl_resources_destroy()
|
||||
void gpu_resources_destroy()
|
||||
{
|
||||
tile.gl_resources_destroy();
|
||||
buffer_object.gl_resources_destroy();
|
||||
tile.gpu_resources_destroy();
|
||||
buffer_object.gpu_resources_destroy();
|
||||
}
|
||||
|
||||
DrawTile tile;
|
||||
GLPixelBufferObject buffer_object;
|
||||
DisplayGPUPixelBuffer buffer_object;
|
||||
bool need_update_texture_pixels = false;
|
||||
};
|
||||
|
||||
@@ -513,36 +485,12 @@ struct BlenderDisplayDriver::Tiles {
|
||||
void gl_resources_destroy_and_clear()
|
||||
{
|
||||
for (DrawTile &tile : tiles) {
|
||||
tile.gl_resources_destroy();
|
||||
tile.gpu_resources_destroy();
|
||||
}
|
||||
|
||||
tiles.clear();
|
||||
}
|
||||
} finished_tiles;
|
||||
|
||||
/* OpenGL vertex buffer needed for drawing. */
|
||||
uint gl_vertex_buffer = 0;
|
||||
|
||||
bool gl_resources_ensure()
|
||||
{
|
||||
if (!gl_vertex_buffer) {
|
||||
glGenBuffers(1, &gl_vertex_buffer);
|
||||
if (!gl_vertex_buffer) {
|
||||
LOG(ERROR) << "Error allocating tile VBO.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gl_resources_destroy()
|
||||
{
|
||||
if (gl_vertex_buffer) {
|
||||
glDeleteBuffers(1, &gl_vertex_buffer);
|
||||
gl_vertex_buffer = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BlenderDisplayDriver::BlenderDisplayDriver(BL::RenderEngine &b_engine,
|
||||
@@ -590,7 +538,7 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms,
|
||||
/* Note that it's the responsibility of BlenderDisplayDriver to ensure updating and drawing
|
||||
* the texture does not happen at the same time. This is achieved indirectly.
|
||||
*
|
||||
* When enabling the OpenGL context, it uses an internal mutex lock DST.gpu_context_lock.
|
||||
* When enabling the OpenGL/GPU context, it uses an internal mutex lock DST.gpu_context_lock.
|
||||
* This same lock is also held when do_draw() is called, which together ensure mutual
|
||||
* exclusion.
|
||||
*
|
||||
@@ -599,12 +547,10 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gl_render_sync_) {
|
||||
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
GPU_fence_wait(gpu_render_sync_);
|
||||
|
||||
DrawTile ¤t_tile = tiles_->current_tile.tile;
|
||||
GLPixelBufferObject ¤t_tile_buffer_object = tiles_->current_tile.buffer_object;
|
||||
DisplayGPUPixelBuffer ¤t_tile_buffer_object = tiles_->current_tile.buffer_object;
|
||||
|
||||
/* Clear storage of all finished tiles when display clear is requested.
|
||||
* Do it when new tile data is provided to handle the display clear flag in a single place.
|
||||
@@ -614,29 +560,14 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms,
|
||||
need_clear_ = false;
|
||||
}
|
||||
|
||||
if (!tiles_->gl_resources_ensure()) {
|
||||
tiles_->gl_resources_destroy();
|
||||
gpu_context_disable();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tiles_->current_tile.gl_resources_ensure()) {
|
||||
tiles_->current_tile.gl_resources_destroy();
|
||||
if (!tiles_->current_tile.gpu_resources_ensure()) {
|
||||
tiles_->current_tile.gpu_resources_destroy();
|
||||
gpu_context_disable();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Update texture dimensions if needed. */
|
||||
if (current_tile.texture.width != texture_width ||
|
||||
current_tile.texture.height != texture_height) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, current_tile.texture.gl_id);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D, 0, GL_RGBA16F, texture_width, texture_height, 0, GL_RGBA, GL_HALF_FLOAT, 0);
|
||||
current_tile.texture.width = texture_width;
|
||||
current_tile.texture.height = texture_height;
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
current_tile.texture.ensure_size(texture_width, texture_height);
|
||||
|
||||
/* Update PBO dimensions if needed.
|
||||
*
|
||||
@@ -649,16 +580,7 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms,
|
||||
* mode faster. */
|
||||
const int buffer_width = params.size.x;
|
||||
const int buffer_height = params.size.y;
|
||||
if (current_tile_buffer_object.width != buffer_width ||
|
||||
current_tile_buffer_object.height != buffer_height) {
|
||||
const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height;
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, current_tile_buffer_object.gl_id);
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, 0, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
current_tile_buffer_object.width = buffer_width;
|
||||
current_tile_buffer_object.height = buffer_height;
|
||||
}
|
||||
current_tile_buffer_object.ensure_size(buffer_width, buffer_height);
|
||||
|
||||
/* Store an updated parameters of the current tile.
|
||||
* In theory it is only needed once per update of the tile, but doing it on every update is
|
||||
@@ -670,19 +592,21 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms,
|
||||
|
||||
static void update_tile_texture_pixels(const DrawTileAndPBO &tile)
|
||||
{
|
||||
const GLTexture &texture = tile.tile.texture;
|
||||
const DisplayGPUTexture &texture = tile.tile.texture;
|
||||
|
||||
DCHECK_NE(tile.buffer_object.gl_id, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.gl_id);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, tile.buffer_object.gl_id);
|
||||
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D, 0, 0, 0, texture.width, texture.height, GL_RGBA, GL_HALF_FLOAT, 0);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
if (!DCHECK_NOTNULL(tile.buffer_object.gpu_pixel_buffer)) {
|
||||
LOG(ERROR) << "Display driver tile pixel buffer unavailable.";
|
||||
return;
|
||||
}
|
||||
GPU_texture_update_sub_from_pixel_buffer(texture.gpu_texture,
|
||||
GPU_DATA_HALF_FLOAT,
|
||||
tile.buffer_object.gpu_pixel_buffer,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
texture.width,
|
||||
texture.height,
|
||||
0);
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::update_end()
|
||||
@@ -709,8 +633,10 @@ void BlenderDisplayDriver::update_end()
|
||||
update_tile_texture_pixels(tiles_->current_tile);
|
||||
}
|
||||
|
||||
gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
/* Ensure GPU fence exists to synchronize upload. */
|
||||
GPU_fence_signal(gpu_upload_sync_);
|
||||
|
||||
GPU_flush();
|
||||
|
||||
gpu_context_disable();
|
||||
}
|
||||
@@ -721,26 +647,26 @@ void BlenderDisplayDriver::update_end()
|
||||
|
||||
half4 *BlenderDisplayDriver::map_texture_buffer()
|
||||
{
|
||||
const uint pbo_gl_id = tiles_->current_tile.buffer_object.gl_id;
|
||||
|
||||
DCHECK_NE(pbo_gl_id, 0);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_gl_id);
|
||||
|
||||
half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(
|
||||
glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
|
||||
GPUPixelBuffer *pix_buf = tiles_->current_tile.buffer_object.gpu_pixel_buffer;
|
||||
if (!DCHECK_NOTNULL(pix_buf)) {
|
||||
LOG(ERROR) << "Display driver tile pixel buffer unavailable.";
|
||||
return nullptr;
|
||||
}
|
||||
half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(GPU_pixel_buffer_map(pix_buf));
|
||||
if (!mapped_rgba_pixels) {
|
||||
LOG(ERROR) << "Error mapping BlenderDisplayDriver pixel buffer object.";
|
||||
}
|
||||
|
||||
return mapped_rgba_pixels;
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::unmap_texture_buffer()
|
||||
{
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
GPUPixelBuffer *pix_buf = tiles_->current_tile.buffer_object.gpu_pixel_buffer;
|
||||
if (!DCHECK_NOTNULL(pix_buf)) {
|
||||
LOG(ERROR) << "Display driver tile pixel buffer unavailable.";
|
||||
return;
|
||||
}
|
||||
GPU_pixel_buffer_unmap(pix_buf);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
@@ -753,7 +679,8 @@ BlenderDisplayDriver::GraphicsInterop BlenderDisplayDriver::graphics_interop_get
|
||||
|
||||
interop_dst.buffer_width = tiles_->current_tile.buffer_object.width;
|
||||
interop_dst.buffer_height = tiles_->current_tile.buffer_object.height;
|
||||
interop_dst.opengl_pbo_id = tiles_->current_tile.buffer_object.gl_id;
|
||||
interop_dst.opengl_pbo_id = (int)GPU_pixel_buffer_get_native_handle(
|
||||
tiles_->current_tile.buffer_object.gpu_pixel_buffer);
|
||||
|
||||
return interop_dst;
|
||||
}
|
||||
@@ -786,7 +713,9 @@ void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y)
|
||||
* This buffer is used to render texture in the viewport.
|
||||
*
|
||||
* NOTE: The buffer needs to be bound. */
|
||||
static void vertex_buffer_update(const DisplayDriver::Params ¶ms)
|
||||
static void vertex_draw(const DisplayDriver::Params ¶ms,
|
||||
int texcoord_attribute,
|
||||
int position_attribute)
|
||||
{
|
||||
const int x = params.full_offset.x;
|
||||
const int y = params.full_offset.y;
|
||||
@@ -794,67 +723,40 @@ static void vertex_buffer_update(const DisplayDriver::Params ¶ms)
|
||||
const int width = params.size.x;
|
||||
const int height = params.size.y;
|
||||
|
||||
/* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be
|
||||
* rendered. */
|
||||
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW);
|
||||
immBegin(GPU_PRIM_TRI_STRIP, 4);
|
||||
|
||||
float *vpointer = reinterpret_cast<float *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
|
||||
if (!vpointer) {
|
||||
return;
|
||||
}
|
||||
immAttr2f(texcoord_attribute, 1.0f, 0.0f);
|
||||
immVertex2f(position_attribute, x + width, y);
|
||||
|
||||
vpointer[0] = 0.0f;
|
||||
vpointer[1] = 0.0f;
|
||||
vpointer[2] = x;
|
||||
vpointer[3] = y;
|
||||
immAttr2f(texcoord_attribute, 1.0f, 1.0f);
|
||||
immVertex2f(position_attribute, x + width, y + height);
|
||||
|
||||
vpointer[4] = 1.0f;
|
||||
vpointer[5] = 0.0f;
|
||||
vpointer[6] = x + width;
|
||||
vpointer[7] = y;
|
||||
immAttr2f(texcoord_attribute, 0.0f, 0.0f);
|
||||
immVertex2f(position_attribute, x, y);
|
||||
|
||||
vpointer[8] = 1.0f;
|
||||
vpointer[9] = 1.0f;
|
||||
vpointer[10] = x + width;
|
||||
vpointer[11] = y + height;
|
||||
immAttr2f(texcoord_attribute, 0.0f, 1.0f);
|
||||
immVertex2f(position_attribute, x, y + height);
|
||||
|
||||
vpointer[12] = 0.0f;
|
||||
vpointer[13] = 1.0f;
|
||||
vpointer[14] = x;
|
||||
vpointer[15] = y + height;
|
||||
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
static void draw_tile(const float2 &zoom,
|
||||
const int texcoord_attribute,
|
||||
const int position_attribute,
|
||||
const DrawTile &draw_tile,
|
||||
const uint gl_vertex_buffer)
|
||||
const DrawTile &draw_tile)
|
||||
{
|
||||
if (!draw_tile.ready_to_draw()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GLTexture &texture = draw_tile.texture;
|
||||
const DisplayGPUTexture &texture = draw_tile.texture;
|
||||
|
||||
DCHECK_NE(texture.gl_id, 0);
|
||||
DCHECK_NE(gl_vertex_buffer, 0);
|
||||
if (!DCHECK_NOTNULL(texture.gpu_texture)) {
|
||||
LOG(ERROR) << "Display driver tile GPU texture resource unavailable.";
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, gl_vertex_buffer);
|
||||
|
||||
/* Draw at the parameters for which the texture has been updated for. This allows to always draw
|
||||
* texture during bordered-rendered camera view without flickering. The validness of the display
|
||||
* parameters for a texture is guaranteed by the initial "clear" state which makes drawing to
|
||||
* have an early output.
|
||||
*
|
||||
* Such approach can cause some extra "jelly" effect during panning, but it is not more jelly
|
||||
* than overlay of selected objects. Also, it's possible to redraw texture at an intersection of
|
||||
* the texture draw parameters and the latest updated draw parameters (although, complexity of
|
||||
* doing it might not worth it. */
|
||||
vertex_buffer_update(draw_tile.params);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture.gl_id);
|
||||
GPU_texture_bind(texture.gpu_texture, 0);
|
||||
|
||||
/* Trick to keep sharp rendering without jagged edges on all GPUs.
|
||||
*
|
||||
@@ -868,26 +770,26 @@ static void draw_tile(const float2 &zoom,
|
||||
const float zoomed_height = draw_tile.params.size.y * zoom.y;
|
||||
if (texture.width != draw_tile.params.size.x || texture.height != draw_tile.params.size.y) {
|
||||
/* Resolution divider is different from 1, force nearest interpolation. */
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
GPU_texture_filter_mode(texture.gpu_texture, false);
|
||||
}
|
||||
else if (zoomed_width - draw_tile.params.size.x > 0.5f ||
|
||||
zoomed_height - draw_tile.params.size.y > 0.5f) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
GPU_texture_filter_mode(texture.gpu_texture, false);
|
||||
}
|
||||
else {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
GPU_texture_filter_mode(texture.gpu_texture, true);
|
||||
}
|
||||
|
||||
glVertexAttribPointer(
|
||||
texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0);
|
||||
glVertexAttribPointer(position_attribute,
|
||||
2,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
4 * sizeof(float),
|
||||
(const GLvoid *)(sizeof(float) * 2));
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
/* Draw at the parameters for which the texture has been updated for. This allows to always draw
|
||||
* texture during bordered-rendered camera view without flickering. The validness of the display
|
||||
* parameters for a texture is guaranteed by the initial "clear" state which makes drawing to
|
||||
* have an early output.
|
||||
*
|
||||
* Such approach can cause some extra "jelly" effect during panning, but it is not more jelly
|
||||
* than overlay of selected objects. Also, it's possible to redraw texture at an intersection of
|
||||
* the texture draw parameters and the latest updated draw parameters (although, complexity of
|
||||
* doing it might not worth it. */
|
||||
vertex_draw(draw_tile.params, texcoord_attribute, position_attribute);
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::flush()
|
||||
@@ -903,13 +805,8 @@ void BlenderDisplayDriver::flush()
|
||||
return;
|
||||
}
|
||||
|
||||
if (gl_upload_sync_) {
|
||||
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
|
||||
if (gl_render_sync_) {
|
||||
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
GPU_fence_wait(gpu_upload_sync_);
|
||||
GPU_fence_wait(gpu_render_sync_);
|
||||
|
||||
gpu_context_disable();
|
||||
}
|
||||
@@ -928,68 +825,56 @@ void BlenderDisplayDriver::draw(const Params ¶ms)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gl_upload_sync_) {
|
||||
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
}
|
||||
GPU_fence_wait(gpu_upload_sync_);
|
||||
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
GPUShader *active_shader = display_shader_->bind(params.full_size.x, params.full_size.y);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
const int texcoord_attribute = GPU_vertformat_attr_add(
|
||||
format, display_shader_->tex_coord_attribute_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
const int position_attribute = GPU_vertformat_attr_add(
|
||||
format, display_shader_->position_attribute_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
|
||||
/* NOTE: The VAO is to be allocated on the drawing context as it is not shared across contexts.
|
||||
* Simplest is to allocate it on every redraw so that it is possible to destroy it from a
|
||||
* correct context. */
|
||||
GLuint vertex_array_object;
|
||||
glGenVertexArrays(1, &vertex_array_object);
|
||||
glBindVertexArray(vertex_array_object);
|
||||
/* Note: Shader is bound again through IMM to register this shader with the imm module
|
||||
* and perform required setup for IMM rendering. This is required as the IMM module
|
||||
* needs to be aware of which shader is bound, and the main display shader
|
||||
* is bound externally. */
|
||||
immBindShader(active_shader);
|
||||
|
||||
display_shader_->bind(params.full_size.x, params.full_size.y);
|
||||
|
||||
const int texcoord_attribute = display_shader_->get_tex_coord_attrib_location();
|
||||
const int position_attribute = display_shader_->get_position_attrib_location();
|
||||
|
||||
glEnableVertexAttribArray(texcoord_attribute);
|
||||
glEnableVertexAttribArray(position_attribute);
|
||||
|
||||
if (tiles_->current_tile.need_update_texture_pixels) {
|
||||
update_tile_texture_pixels(tiles_->current_tile);
|
||||
tiles_->current_tile.need_update_texture_pixels = false;
|
||||
}
|
||||
|
||||
draw_tile(zoom_,
|
||||
texcoord_attribute,
|
||||
position_attribute,
|
||||
tiles_->current_tile.tile,
|
||||
tiles_->gl_vertex_buffer);
|
||||
draw_tile(zoom_, texcoord_attribute, position_attribute, tiles_->current_tile.tile);
|
||||
|
||||
for (const DrawTile &tile : tiles_->finished_tiles.tiles) {
|
||||
draw_tile(zoom_, texcoord_attribute, position_attribute, tile, tiles_->gl_vertex_buffer);
|
||||
draw_tile(zoom_, texcoord_attribute, position_attribute, tile);
|
||||
}
|
||||
|
||||
/* Reset IMM shader bind state. */
|
||||
immUnbindProgram();
|
||||
|
||||
display_shader_->unbind();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
||||
glDeleteVertexArrays(1, &vertex_array_object);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
GPU_fence_signal(gpu_render_sync_);
|
||||
GPU_flush();
|
||||
|
||||
gpu_context_unlock();
|
||||
|
||||
VLOG_DEVICE_STATS << "Display driver number of textures: " << GLTexture::num_used;
|
||||
VLOG_DEVICE_STATS << "Display driver number of PBOs: " << GLPixelBufferObject::num_used;
|
||||
VLOG_DEVICE_STATS << "Display driver number of textures: " << DisplayGPUTexture::num_used;
|
||||
VLOG_DEVICE_STATS << "Display driver number of PBOs: " << DisplayGPUPixelBuffer::num_used;
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::gpu_context_create()
|
||||
{
|
||||
if (!RE_engine_gpu_context_create(reinterpret_cast<RenderEngine *>(b_engine_.ptr.data))) {
|
||||
LOG(ERROR) << "Error creating OpenGL context.";
|
||||
LOG(ERROR) << "Error creating GPU context.";
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create global GPU resources for display driver. */
|
||||
if (!gpu_resources_create()) {
|
||||
LOG(ERROR) << "Error creating GPU resources for Cycles Display Driver.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1018,13 +903,43 @@ void BlenderDisplayDriver::gpu_context_unlock()
|
||||
RE_engine_gpu_context_unlock(reinterpret_cast<RenderEngine *>(b_engine_.ptr.data));
|
||||
}
|
||||
|
||||
bool BlenderDisplayDriver::gpu_resources_create()
|
||||
{
|
||||
/* Ensure context is active for resource creation. */
|
||||
if (!gpu_context_enable()) {
|
||||
LOG(ERROR) << "Error enabling GPU context.";
|
||||
return false;
|
||||
}
|
||||
|
||||
gpu_upload_sync_ = GPU_fence_create();
|
||||
gpu_render_sync_ = GPU_fence_create();
|
||||
|
||||
if (!DCHECK_NOTNULL(gpu_upload_sync_) || !DCHECK_NOTNULL(gpu_render_sync_)) {
|
||||
LOG(ERROR) << "Error creating GPU synchronization primtiives.";
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
gpu_context_disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlenderDisplayDriver::gpu_resources_destroy()
|
||||
{
|
||||
gpu_context_enable();
|
||||
|
||||
tiles_->current_tile.gl_resources_destroy();
|
||||
tiles_->current_tile.gpu_resources_destroy();
|
||||
tiles_->finished_tiles.gl_resources_destroy_and_clear();
|
||||
tiles_->gl_resources_destroy();
|
||||
|
||||
/* Fences. */
|
||||
if (gpu_render_sync_) {
|
||||
GPU_fence_free(gpu_render_sync_);
|
||||
gpu_render_sync_ = nullptr;
|
||||
}
|
||||
if (gpu_upload_sync_) {
|
||||
GPU_fence_free(gpu_upload_sync_);
|
||||
gpu_upload_sync_ = nullptr;
|
||||
}
|
||||
|
||||
gpu_context_disable();
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include "util/unique_ptr.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
typedef struct GPUContext GPUContext;
|
||||
typedef struct GPUFence GPUFence;
|
||||
typedef struct GPUShader GPUShader;
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Base class of shader used for display driver rendering. */
|
||||
@@ -29,7 +33,7 @@ class BlenderDisplayShader {
|
||||
BlenderDisplayShader() = default;
|
||||
virtual ~BlenderDisplayShader() = default;
|
||||
|
||||
virtual void bind(int width, int height) = 0;
|
||||
virtual GPUShader *bind(int width, int height) = 0;
|
||||
virtual void unbind() = 0;
|
||||
|
||||
/* Get attribute location for position and texture coordinate respectively.
|
||||
@@ -40,7 +44,7 @@ class BlenderDisplayShader {
|
||||
protected:
|
||||
/* Get program of this display shader.
|
||||
* NOTE: The shader needs to be bound to have access to this. */
|
||||
virtual uint get_shader_program() = 0;
|
||||
virtual GPUShader *get_shader_program() = 0;
|
||||
|
||||
/* Cached values of various OpenGL resources. */
|
||||
int position_attribute_location_ = -1;
|
||||
@@ -51,16 +55,16 @@ class BlenderDisplayShader {
|
||||
* display space shader. */
|
||||
class BlenderFallbackDisplayShader : public BlenderDisplayShader {
|
||||
public:
|
||||
virtual void bind(int width, int height) override;
|
||||
virtual GPUShader *bind(int width, int height) override;
|
||||
virtual void unbind() override;
|
||||
|
||||
protected:
|
||||
virtual uint get_shader_program() override;
|
||||
virtual GPUShader *get_shader_program() override;
|
||||
|
||||
void create_shader_if_needed();
|
||||
void destroy_shader();
|
||||
|
||||
uint shader_program_ = 0;
|
||||
GPUShader *shader_program_ = 0;
|
||||
int image_texture_location_ = -1;
|
||||
int fullscreen_location_ = -1;
|
||||
|
||||
@@ -73,17 +77,17 @@ class BlenderDisplaySpaceShader : public BlenderDisplayShader {
|
||||
public:
|
||||
BlenderDisplaySpaceShader(BL::RenderEngine &b_engine, BL::Scene &b_scene);
|
||||
|
||||
virtual void bind(int width, int height) override;
|
||||
virtual GPUShader *bind(int width, int height) override;
|
||||
virtual void unbind() override;
|
||||
|
||||
protected:
|
||||
virtual uint get_shader_program() override;
|
||||
virtual GPUShader *get_shader_program() override;
|
||||
|
||||
BL::RenderEngine b_engine_;
|
||||
BL::Scene &b_scene_;
|
||||
|
||||
/* Cached values of various OpenGL resources. */
|
||||
uint shader_program_ = 0;
|
||||
GPUShader *shader_program_ = nullptr;
|
||||
};
|
||||
|
||||
/* Display driver implementation which is specific for Blender viewport integration. */
|
||||
@@ -122,6 +126,9 @@ class BlenderDisplayDriver : public DisplayDriver {
|
||||
void gpu_context_lock();
|
||||
void gpu_context_unlock();
|
||||
|
||||
/* Create GPU resources used by the dispaly driver. */
|
||||
bool gpu_resources_create();
|
||||
|
||||
/* Destroy all GPU resources which are being used by this object. */
|
||||
void gpu_resources_destroy();
|
||||
|
||||
@@ -137,8 +144,8 @@ class BlenderDisplayDriver : public DisplayDriver {
|
||||
struct Tiles;
|
||||
unique_ptr<Tiles> tiles_;
|
||||
|
||||
void *gl_render_sync_ = nullptr;
|
||||
void *gl_upload_sync_ = nullptr;
|
||||
GPUFence *gpu_render_sync_ = nullptr;
|
||||
GPUFence *gpu_upload_sync_ = nullptr;
|
||||
|
||||
float2 zoom_ = make_float2(1.0f, 1.0f);
|
||||
};
|
||||
|
||||
@@ -97,6 +97,7 @@ void GPU_shader_free(GPUShader *shader);
|
||||
|
||||
void GPU_shader_bind(GPUShader *shader);
|
||||
void GPU_shader_unbind(void);
|
||||
GPUShader *GPU_shader_get_bound(void);
|
||||
|
||||
const char *GPU_shader_get_name(GPUShader *shader);
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
/** Opaque type hiding blender::gpu::Fence. */
|
||||
typedef struct GPUFence GPUFence;
|
||||
|
||||
typedef enum eGPUWriteMask {
|
||||
GPU_WRITE_NONE = 0,
|
||||
GPU_WRITE_RED = (1 << 0),
|
||||
@@ -196,6 +199,11 @@ bool GPU_bgl_get(void);
|
||||
|
||||
void GPU_memory_barrier(eGPUBarrier barrier);
|
||||
|
||||
GPUFence *GPU_fence_create(void);
|
||||
void GPU_fence_free(GPUFence *fence);
|
||||
void GPU_fence_signal(GPUFence *fence);
|
||||
void GPU_fence_wait(GPUFence *fence);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,9 @@ struct GPUVertBuf;
|
||||
/** Opaque type hiding blender::gpu::Texture. */
|
||||
typedef struct GPUTexture GPUTexture;
|
||||
|
||||
/** Opaque type hiding blender::gpu::PixelBuffer. */
|
||||
typedef struct GPUPixelBuffer GPUPixelBuffer;
|
||||
|
||||
/**
|
||||
* GPU Samplers state
|
||||
* - Specify the sampler state to bind a texture with.
|
||||
@@ -284,6 +287,17 @@ void GPU_texture_update_sub(GPUTexture *tex,
|
||||
int width,
|
||||
int height,
|
||||
int depth);
|
||||
|
||||
/* Update from API Buffer. */
|
||||
void GPU_texture_update_sub_from_pixel_buffer(GPUTexture *tex,
|
||||
eGPUDataFormat data_format,
|
||||
GPUPixelBuffer *pix_buf,
|
||||
int offset_x,
|
||||
int offset_y,
|
||||
int offset_z,
|
||||
int width,
|
||||
int height,
|
||||
int depth);
|
||||
/**
|
||||
* Makes data interpretation aware of the source layout.
|
||||
* Skipping pixels correctly when changing rows when doing partial update.
|
||||
@@ -366,6 +380,15 @@ void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size);
|
||||
size_t GPU_texture_component_len(eGPUTextureFormat format);
|
||||
size_t GPU_texture_dataformat_size(eGPUDataFormat data_format);
|
||||
|
||||
/* GPU Pixel Buffer. */
|
||||
GPUPixelBuffer *GPU_pixel_buffer_create(uint size);
|
||||
void GPU_pixel_buffer_free(GPUPixelBuffer *pix_buf);
|
||||
|
||||
void *GPU_pixel_buffer_map(GPUPixelBuffer *pix_buf);
|
||||
void GPU_pixel_buffer_unmap(GPUPixelBuffer *pix_buf);
|
||||
uint GPU_pixel_buffer_size(GPUPixelBuffer *pix_buf);
|
||||
int64_t GPU_pixel_buffer_get_native_handle(GPUPixelBuffer *pix_buf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -18,8 +18,10 @@ class Context;
|
||||
|
||||
class Batch;
|
||||
class DrawList;
|
||||
class Fence;
|
||||
class FrameBuffer;
|
||||
class IndexBuf;
|
||||
class PixelBuffer;
|
||||
class QueryPool;
|
||||
class Shader;
|
||||
class Texture;
|
||||
@@ -42,8 +44,10 @@ class GPUBackend {
|
||||
|
||||
virtual Batch *batch_alloc() = 0;
|
||||
virtual DrawList *drawlist_alloc(int list_length) = 0;
|
||||
virtual Fence *fence_alloc() = 0;
|
||||
virtual FrameBuffer *framebuffer_alloc(const char *name) = 0;
|
||||
virtual IndexBuf *indexbuf_alloc() = 0;
|
||||
virtual PixelBuffer *pixelbuf_alloc(uint size) = 0;
|
||||
virtual QueryPool *querypool_alloc() = 0;
|
||||
virtual Shader *shader_alloc(const char *name) = 0;
|
||||
virtual Texture *texture_alloc(const char *name) = 0;
|
||||
|
||||
@@ -527,6 +527,15 @@ void GPU_shader_unbind()
|
||||
#endif
|
||||
}
|
||||
|
||||
GPUShader *GPU_shader_get_bound()
|
||||
{
|
||||
Context *ctx = Context::get();
|
||||
if (ctx) {
|
||||
return wrap(ctx->shader);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "GPU_state.h"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "gpu_state_private.hh"
|
||||
@@ -373,6 +374,27 @@ void GPU_memory_barrier(eGPUBarrier barrier)
|
||||
Context::get()->state_manager->issue_barrier(barrier);
|
||||
}
|
||||
|
||||
GPUFence *GPU_fence_create()
|
||||
{
|
||||
Fence *fence = GPUBackend::get()->fence_alloc();
|
||||
return wrap(fence);
|
||||
}
|
||||
|
||||
void GPU_fence_free(GPUFence *fence)
|
||||
{
|
||||
delete unwrap(fence);
|
||||
}
|
||||
|
||||
void GPU_fence_signal(GPUFence *fence)
|
||||
{
|
||||
unwrap(fence)->signal();
|
||||
}
|
||||
|
||||
void GPU_fence_wait(GPUFence *fence)
|
||||
{
|
||||
unwrap(fence)->wait();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -160,5 +160,34 @@ class StateManager {
|
||||
virtual void texture_unpack_row_length_set(uint len) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* GPUFence.
|
||||
*/
|
||||
class Fence {
|
||||
protected:
|
||||
bool signalled_ = false;
|
||||
|
||||
public:
|
||||
Fence(){};
|
||||
virtual ~Fence(){};
|
||||
|
||||
virtual void signal() = 0;
|
||||
virtual void wait() = 0;
|
||||
};
|
||||
|
||||
/* Syntactic sugar. */
|
||||
static inline GPUFence *wrap(Fence *pixbuf)
|
||||
{
|
||||
return reinterpret_cast<GPUFence *>(pixbuf);
|
||||
}
|
||||
static inline Fence *unwrap(GPUFence *pixbuf)
|
||||
{
|
||||
return reinterpret_cast<Fence *>(pixbuf);
|
||||
}
|
||||
static inline const Fence *unwrap(const GPUFence *pixbuf)
|
||||
{
|
||||
return reinterpret_cast<const Fence *>(pixbuf);
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
|
||||
@@ -455,6 +455,21 @@ void GPU_texture_update_sub(GPUTexture *tex,
|
||||
reinterpret_cast<Texture *>(tex)->update_sub(0, offset, extent, data_format, pixels);
|
||||
}
|
||||
|
||||
void GPU_texture_update_sub_from_pixel_buffer(GPUTexture *tex,
|
||||
eGPUDataFormat data_format,
|
||||
GPUPixelBuffer *pix_buf,
|
||||
int offset_x,
|
||||
int offset_y,
|
||||
int offset_z,
|
||||
int width,
|
||||
int height,
|
||||
int depth)
|
||||
{
|
||||
int offset[3] = {offset_x, offset_y, offset_z};
|
||||
int extent[3] = {width, height, depth};
|
||||
reinterpret_cast<Texture *>(tex)->update_sub(offset, extent, data_format, pix_buf);
|
||||
}
|
||||
|
||||
void *GPU_texture_read(GPUTexture *tex_, eGPUDataFormat data_format, int miplvl)
|
||||
{
|
||||
Texture *tex = reinterpret_cast<Texture *>(tex_);
|
||||
@@ -824,6 +839,53 @@ void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *r_size)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Pixel Buffer
|
||||
*
|
||||
* Pixel buffer utility functions.
|
||||
* \{ */
|
||||
|
||||
GPUPixelBuffer *GPU_pixel_buffer_create(uint size)
|
||||
{
|
||||
/* Ensure buffer satifies the alignment of 256 bytes for copying
|
||||
* data between buffers and textures. As specified in:
|
||||
* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
||||
*
|
||||
* Ensuring minimal size across all platforms handles cases for small-sized
|
||||
* textures and avoids issues with zero-sized buffers. */
|
||||
size = ceil_to_multiple_ul(size, 256);
|
||||
PixelBuffer *pixbuf = GPUBackend::get()->pixelbuf_alloc(size);
|
||||
return wrap(pixbuf);
|
||||
}
|
||||
|
||||
void GPU_pixel_buffer_free(GPUPixelBuffer *pix_buf)
|
||||
{
|
||||
PixelBuffer *handle = unwrap(pix_buf);
|
||||
delete handle;
|
||||
}
|
||||
|
||||
void *GPU_pixel_buffer_map(GPUPixelBuffer *pix_buf)
|
||||
{
|
||||
return reinterpret_cast<PixelBuffer *>(pix_buf)->map();
|
||||
}
|
||||
|
||||
void GPU_pixel_buffer_unmap(GPUPixelBuffer *pix_buf)
|
||||
{
|
||||
reinterpret_cast<PixelBuffer *>(pix_buf)->unmap();
|
||||
}
|
||||
|
||||
uint GPU_pixel_buffer_size(GPUPixelBuffer *pix_buf)
|
||||
{
|
||||
return reinterpret_cast<PixelBuffer *>(pix_buf)->get_size();
|
||||
}
|
||||
|
||||
int64_t GPU_pixel_buffer_get_native_handle(GPUPixelBuffer *pix_buf)
|
||||
{
|
||||
return reinterpret_cast<PixelBuffer *>(pix_buf)->get_native_handle();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Sampler Objects
|
||||
*
|
||||
|
||||
@@ -129,6 +129,10 @@ class Texture {
|
||||
|
||||
virtual void update_sub(
|
||||
int mip, int offset[3], int extent[3], eGPUDataFormat format, const void *data) = 0;
|
||||
virtual void update_sub(int offset[3],
|
||||
int extent[3],
|
||||
eGPUDataFormat format,
|
||||
GPUPixelBuffer *pixbuf) = 0;
|
||||
|
||||
/* TODO(fclem): Legacy. Should be removed at some point. */
|
||||
virtual uint gl_bindcode_get() const = 0;
|
||||
@@ -264,6 +268,35 @@ static inline const Texture *unwrap(const GPUTexture *vert)
|
||||
return reinterpret_cast<const Texture *>(vert);
|
||||
}
|
||||
|
||||
/* GPU pixel Buffer. */
|
||||
class PixelBuffer {
|
||||
protected:
|
||||
uint size_ = 0;
|
||||
|
||||
public:
|
||||
PixelBuffer(uint size) : size_(size){};
|
||||
virtual ~PixelBuffer(){};
|
||||
|
||||
virtual void *map() = 0;
|
||||
virtual void unmap() = 0;
|
||||
virtual int64_t get_native_handle() = 0;
|
||||
virtual uint get_size() = 0;
|
||||
};
|
||||
|
||||
/* Syntactic sugar. */
|
||||
static inline GPUPixelBuffer *wrap(PixelBuffer *pixbuf)
|
||||
{
|
||||
return reinterpret_cast<GPUPixelBuffer *>(pixbuf);
|
||||
}
|
||||
static inline PixelBuffer *unwrap(GPUPixelBuffer *pixbuf)
|
||||
{
|
||||
return reinterpret_cast<PixelBuffer *>(pixbuf);
|
||||
}
|
||||
static inline const PixelBuffer *unwrap(const GPUPixelBuffer *pixbuf)
|
||||
{
|
||||
return reinterpret_cast<const PixelBuffer *>(pixbuf);
|
||||
}
|
||||
|
||||
#undef DEBUG_NAME_LEN
|
||||
|
||||
inline size_t to_bytesize(eGPUTextureFormat format)
|
||||
@@ -405,6 +438,8 @@ inline size_t to_bytesize(eGPUDataFormat data_format)
|
||||
switch (data_format) {
|
||||
case GPU_DATA_UBYTE:
|
||||
return 1;
|
||||
case GPU_DATA_HALF_FLOAT:
|
||||
return 2;
|
||||
case GPU_DATA_FLOAT:
|
||||
case GPU_DATA_INT:
|
||||
case GPU_DATA_UINT:
|
||||
|
||||
@@ -66,8 +66,10 @@ class MTLBackend : public GPUBackend {
|
||||
Context *context_alloc(void *ghost_window, void *ghost_context) override;
|
||||
Batch *batch_alloc() override;
|
||||
DrawList *drawlist_alloc(int list_length) override;
|
||||
Fence *fence_alloc() override;
|
||||
FrameBuffer *framebuffer_alloc(const char *name) override;
|
||||
IndexBuf *indexbuf_alloc() override;
|
||||
PixelBuffer *pixelbuf_alloc(uint size) override;
|
||||
QueryPool *querypool_alloc() override;
|
||||
Shader *shader_alloc(const char *name) override;
|
||||
Texture *texture_alloc(const char *name) override;
|
||||
|
||||
@@ -55,6 +55,11 @@ DrawList *MTLBackend::drawlist_alloc(int list_length)
|
||||
return new MTLDrawList(list_length);
|
||||
};
|
||||
|
||||
Fence *MTLBackend::fence_alloc()
|
||||
{
|
||||
return new MTLFence();
|
||||
};
|
||||
|
||||
FrameBuffer *MTLBackend::framebuffer_alloc(const char *name)
|
||||
{
|
||||
MTLContext *mtl_context = static_cast<MTLContext *>(
|
||||
@@ -67,6 +72,11 @@ IndexBuf *MTLBackend::indexbuf_alloc()
|
||||
return new MTLIndexBuf();
|
||||
};
|
||||
|
||||
PixelBuffer *MTLBackend::pixelbuf_alloc(uint size)
|
||||
{
|
||||
return new MTLPixelBuffer(size);
|
||||
};
|
||||
|
||||
QueryPool *MTLBackend::querypool_alloc()
|
||||
{
|
||||
return new MTLQueryPool();
|
||||
|
||||
@@ -538,6 +538,24 @@ bool MTLCommandBufferManager::insert_memory_barrier(eGPUBarrier barrier_bits,
|
||||
return false;
|
||||
}
|
||||
|
||||
void MTLCommandBufferManager::encode_signal_event(id<MTLEvent> event, uint64_t signal_value)
|
||||
{
|
||||
/* Ensure active command buffer. */
|
||||
id<MTLCommandBuffer> cmd_buf = this->ensure_begin();
|
||||
BLI_assert(cmd_buf);
|
||||
this->end_active_command_encoder();
|
||||
[cmd_buf encodeSignalEvent:event value:signal_value];
|
||||
}
|
||||
|
||||
void MTLCommandBufferManager::encode_wait_for_event(id<MTLEvent> event, uint64_t signal_value)
|
||||
{
|
||||
/* Ensure active command buffer. */
|
||||
id<MTLCommandBuffer> cmd_buf = this->ensure_begin();
|
||||
BLI_assert(cmd_buf);
|
||||
this->end_active_command_encoder();
|
||||
[cmd_buf encodeWaitForEvent:event value:signal_value];
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -555,6 +555,8 @@ class MTLCommandBufferManager {
|
||||
bool insert_memory_barrier(eGPUBarrier barrier_bits,
|
||||
eGPUStageBarrierBits before_stages,
|
||||
eGPUStageBarrierBits after_stages);
|
||||
void encode_signal_event(id<MTLEvent> event, uint64_t value);
|
||||
void encode_wait_for_event(id<MTLEvent> event, uint64_t value);
|
||||
/* TODO(Metal): Support fences in command buffer class. */
|
||||
|
||||
/* Debug. */
|
||||
|
||||
@@ -84,4 +84,24 @@ class MTLStateManager : public StateManager {
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("MTLStateManager")
|
||||
};
|
||||
|
||||
/* Fence synchronization primitive. */
|
||||
class MTLFence : public Fence {
|
||||
private:
|
||||
/* Using an event in this instance, as this is global for the command stream, rather than being
|
||||
* inserted at the encoder level. This has the behaviour to match the GL functionality. */
|
||||
id<MTLEvent> mtl_event_ = nil;
|
||||
/* Events can be re-used multiple times. We can track a counter flagging the latest value
|
||||
* signalled. */
|
||||
uint64_t last_signalled_value_ = 0;
|
||||
|
||||
public:
|
||||
MTLFence() : Fence(){};
|
||||
~MTLFence();
|
||||
|
||||
void signal() override;
|
||||
void wait() override;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("MTLFence")
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
||||
@@ -560,6 +560,44 @@ void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits)
|
||||
ctx->main_command_buffer.insert_memory_barrier(barrier_bits, before_stages, after_stages);
|
||||
}
|
||||
|
||||
MTLFence::~MTLFence()
|
||||
{
|
||||
if (mtl_event_) {
|
||||
[mtl_event_ release];
|
||||
mtl_event_ = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void MTLFence::signal()
|
||||
{
|
||||
if (mtl_event_ == nil) {
|
||||
MTLContext *ctx = MTLContext::get();
|
||||
BLI_assert(ctx);
|
||||
mtl_event_ = [ctx->device newEvent];
|
||||
}
|
||||
MTLContext *ctx = MTLContext::get();
|
||||
BLI_assert(ctx);
|
||||
ctx->main_command_buffer.encode_signal_event(mtl_event_, ++last_signalled_value_);
|
||||
|
||||
signalled_ = true;
|
||||
}
|
||||
|
||||
void MTLFence::wait()
|
||||
{
|
||||
/* do not attempt to wait if event has not yet been signalled for the first time. */
|
||||
if (mtl_event_ == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (signalled_) {
|
||||
MTLContext *ctx = MTLContext::get();
|
||||
BLI_assert(ctx);
|
||||
|
||||
ctx->main_command_buffer.encode_wait_for_event(mtl_event_, last_signalled_value_);
|
||||
signalled_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -235,6 +235,10 @@ class MTLTexture : public Texture {
|
||||
|
||||
void update_sub(
|
||||
int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data) override;
|
||||
void update_sub(int offset[3],
|
||||
int extent[3],
|
||||
eGPUDataFormat format,
|
||||
GPUPixelBuffer *pixbuf) override;
|
||||
|
||||
void generate_mipmap() override;
|
||||
void copy_to(Texture *dst) override;
|
||||
@@ -424,6 +428,24 @@ class MTLTexture : public Texture {
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("MTLTexture")
|
||||
};
|
||||
|
||||
class MTLPixelBuffer : public PixelBuffer {
|
||||
private:
|
||||
id<MTLBuffer> buffer_ = nil;
|
||||
|
||||
public:
|
||||
MTLPixelBuffer(uint size);
|
||||
~MTLPixelBuffer();
|
||||
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
int64_t get_native_handle() override;
|
||||
uint get_size() override;
|
||||
|
||||
id<MTLBuffer> get_metal_buffer();
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("MTLPixelBuffer")
|
||||
};
|
||||
|
||||
/* Utility */
|
||||
MTLPixelFormat gpu_texture_format_to_metal(eGPUTextureFormat tex_format);
|
||||
int get_mtl_format_bytesize(MTLPixelFormat tex_format);
|
||||
|
||||
@@ -885,6 +885,61 @@ void gpu::MTLTexture::update_sub(
|
||||
}
|
||||
}
|
||||
|
||||
void MTLTexture::update_sub(int offset[3],
|
||||
int extent[3],
|
||||
eGPUDataFormat format,
|
||||
GPUPixelBuffer *pixbuf)
|
||||
{
|
||||
/* Update texture from pixel buffer. */
|
||||
BLI_assert(validate_data_format(format_, format));
|
||||
BLI_assert(pixbuf != nullptr);
|
||||
|
||||
/* Fetch pixel buffer metal buffer. */
|
||||
MTLPixelBuffer *mtl_pix_buf = static_cast<MTLPixelBuffer *>(unwrap(pixbuf));
|
||||
id<MTLBuffer> buffer = mtl_pix_buf->get_metal_buffer();
|
||||
BLI_assert(buffer != nil);
|
||||
if (buffer == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure texture is ready. */
|
||||
this->ensure_baked();
|
||||
BLI_assert(texture_ != nil);
|
||||
|
||||
/* Calculate dimensions. */
|
||||
int num_image_channels = to_component_len(format_);
|
||||
|
||||
uint bits_per_pixel = num_image_channels * to_bytesize(format);
|
||||
uint bytes_per_row = bits_per_pixel * extent[0];
|
||||
uint bytes_per_image = bytes_per_row * extent[1];
|
||||
|
||||
/* Currently only required for 2D textures. */
|
||||
if (type_ == GPU_TEXTURE_2D) {
|
||||
|
||||
/* Create blit command encoder to copy data. */
|
||||
MTLContext *ctx = MTLContext::get();
|
||||
BLI_assert(ctx);
|
||||
|
||||
id<MTLBlitCommandEncoder> blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
|
||||
[blit_encoder copyFromBuffer:buffer
|
||||
sourceOffset:0
|
||||
sourceBytesPerRow:bytes_per_row
|
||||
sourceBytesPerImage:bytes_per_image
|
||||
sourceSize:MTLSizeMake(extent[0], extent[1], 1)
|
||||
toTexture:texture_
|
||||
destinationSlice:0
|
||||
destinationLevel:0
|
||||
destinationOrigin:MTLOriginMake(offset[0], offset[1], 0)];
|
||||
|
||||
if (texture_.storageMode == MTLStorageModeManaged) {
|
||||
[blit_encoder synchronizeResource:texture_];
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void gpu::MTLTexture::ensure_mipmaps(int miplvl)
|
||||
{
|
||||
|
||||
@@ -1797,4 +1852,74 @@ void gpu::MTLTexture::reset()
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pixel Buffer
|
||||
* \{ */
|
||||
|
||||
MTLPixelBuffer::MTLPixelBuffer(uint size) : PixelBuffer(size)
|
||||
{
|
||||
MTLContext *ctx = MTLContext::get();
|
||||
BLI_assert(ctx);
|
||||
/* Ensure buffer satifies the alignment of 256 bytes for copying
|
||||
* data between buffers and textures. As specified in:
|
||||
* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */
|
||||
BLI_assert(size >= 256);
|
||||
|
||||
MTLResourceOptions resource_options = ([ctx->device hasUnifiedMemory]) ?
|
||||
MTLResourceStorageModeShared :
|
||||
MTLResourceStorageModeManaged;
|
||||
buffer_ = [ctx->device newBufferWithLength:size options:resource_options];
|
||||
BLI_assert(buffer_ != nil);
|
||||
}
|
||||
|
||||
MTLPixelBuffer::~MTLPixelBuffer()
|
||||
{
|
||||
if (buffer_) {
|
||||
[buffer_ release];
|
||||
buffer_ = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void *MTLPixelBuffer::map()
|
||||
{
|
||||
if (buffer_ == nil) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return [buffer_ contents];
|
||||
}
|
||||
|
||||
void MTLPixelBuffer::unmap()
|
||||
{
|
||||
if (buffer_ == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure changes are synchronized. */
|
||||
if (buffer_.resourceOptions & MTLResourceStorageModeManaged) {
|
||||
[buffer_ didModifyRange:NSMakeRange(0, size_)];
|
||||
}
|
||||
}
|
||||
|
||||
int64_t MTLPixelBuffer::get_native_handle()
|
||||
{
|
||||
if (buffer_ == nil) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return reinterpret_cast<int64_t>(buffer_);
|
||||
}
|
||||
|
||||
uint MTLPixelBuffer::get_size()
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
id<MTLBuffer> MTLPixelBuffer::get_metal_buffer()
|
||||
{
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
||||
@@ -76,6 +76,11 @@ class GLBackend : public GPUBackend {
|
||||
return new GLDrawList(list_length);
|
||||
};
|
||||
|
||||
Fence *fence_alloc() override
|
||||
{
|
||||
return new GLFence();
|
||||
};
|
||||
|
||||
FrameBuffer *framebuffer_alloc(const char *name) override
|
||||
{
|
||||
return new GLFrameBuffer(name);
|
||||
@@ -86,6 +91,11 @@ class GLBackend : public GPUBackend {
|
||||
return new GLIndexBuf();
|
||||
};
|
||||
|
||||
PixelBuffer *pixelbuf_alloc(uint size) override
|
||||
{
|
||||
return new GLPixelBuffer(size);
|
||||
};
|
||||
|
||||
QueryPool *querypool_alloc() override
|
||||
{
|
||||
return new GLQueryPool();
|
||||
|
||||
@@ -641,6 +641,34 @@ void GLStateManager::issue_barrier(eGPUBarrier barrier_bits)
|
||||
glMemoryBarrier(to_gl(barrier_bits));
|
||||
}
|
||||
|
||||
GLFence::~GLFence()
|
||||
{
|
||||
if (gl_sync_ != 0) {
|
||||
glDeleteSync(gl_sync_);
|
||||
gl_sync_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GLFence::signal()
|
||||
{
|
||||
/* If fence is already signalled, create a newly signalled fence primitive. */
|
||||
if (gl_sync_) {
|
||||
glDeleteSync(gl_sync_);
|
||||
}
|
||||
|
||||
gl_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
signalled_ = true;
|
||||
}
|
||||
|
||||
void GLFence::wait()
|
||||
{
|
||||
/* Do not wait if fence does not yet exist. */
|
||||
if (gl_sync_ == 0) {
|
||||
return;
|
||||
}
|
||||
glWaitSync(gl_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
signalled_ = false;
|
||||
}
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
||||
@@ -103,6 +103,21 @@ class GLStateManager : public StateManager {
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLStateManager")
|
||||
};
|
||||
|
||||
/* Fence synchronization primitive. */
|
||||
class GLFence : public Fence {
|
||||
private:
|
||||
GLsync gl_sync_ = 0;
|
||||
|
||||
public:
|
||||
GLFence() : Fence(){};
|
||||
~GLFence();
|
||||
|
||||
void signal() override;
|
||||
void wait() override;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLFence")
|
||||
};
|
||||
|
||||
static inline GLbitfield to_gl(eGPUBarrier barrier_bits)
|
||||
{
|
||||
GLbitfield barrier = 0;
|
||||
|
||||
@@ -303,6 +303,42 @@ void GLTexture::update_sub(
|
||||
has_pixels_ = true;
|
||||
}
|
||||
|
||||
void GLTexture::update_sub(int offset[3],
|
||||
int extent[3],
|
||||
eGPUDataFormat format,
|
||||
GPUPixelBuffer *pixbuf)
|
||||
{
|
||||
/* Update texture from pixel buffer. */
|
||||
BLI_assert(validate_data_format(format_, format));
|
||||
BLI_assert(pixbuf != nullptr);
|
||||
|
||||
const int dimensions = this->dimensions_count();
|
||||
GLenum gl_format = to_gl_data_format(format_);
|
||||
GLenum gl_type = to_gl(format);
|
||||
|
||||
/* Temporarily Bind texture. */
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
|
||||
/* Bind pixel buffer for source data. */
|
||||
int pix_buf_handle = (int)GPU_pixel_buffer_get_native_handle(pixbuf);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pix_buf_handle);
|
||||
|
||||
switch (dimensions) {
|
||||
default:
|
||||
case 1:
|
||||
glTexSubImage1D(target_, 0, offset[0], extent[0], gl_format, gl_type, 0);
|
||||
break;
|
||||
case 2:
|
||||
glTexSubImage2D(target_, 0, UNPACK2(offset), UNPACK2(extent), gl_format, gl_type, 0);
|
||||
break;
|
||||
case 3:
|
||||
glTexSubImage3D(target_, 0, UNPACK3(offset), UNPACK3(extent), gl_format, gl_type, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will create the mipmap images and populate them with filtered data from base level.
|
||||
*
|
||||
@@ -739,4 +775,63 @@ uint GLTexture::gl_bindcode_get() const
|
||||
return tex_id_;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pixel Buffer
|
||||
* \{ */
|
||||
|
||||
GLPixelBuffer::GLPixelBuffer(uint size) : PixelBuffer(size)
|
||||
{
|
||||
glGenBuffers(1, &gl_id_);
|
||||
BLI_assert(gl_id_);
|
||||
|
||||
if (!gl_id_) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure size is non-zero for pixel buffer backing storage creation. */
|
||||
size = max_ii(size, 32);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_id_);
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, size, 0, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
GLPixelBuffer::~GLPixelBuffer()
|
||||
{
|
||||
if (!gl_id_) {
|
||||
return;
|
||||
}
|
||||
glDeleteBuffers(1, &gl_id_);
|
||||
}
|
||||
|
||||
void *GLPixelBuffer::map()
|
||||
{
|
||||
if (!gl_id_) {
|
||||
BLI_assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_id_);
|
||||
void *ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
BLI_assert(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void GLPixelBuffer::unmap()
|
||||
{
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
int64_t GLPixelBuffer::get_native_handle()
|
||||
{
|
||||
return (int64_t)gl_id_;
|
||||
}
|
||||
|
||||
uint GLPixelBuffer::get_size()
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
} // namespace blender::gpu
|
||||
|
||||
@@ -46,6 +46,10 @@ class GLTexture : public Texture {
|
||||
|
||||
void update_sub(
|
||||
int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data) override;
|
||||
void update_sub(int offset[3],
|
||||
int extent[3],
|
||||
eGPUDataFormat format,
|
||||
GPUPixelBuffer *pixbuf) override;
|
||||
|
||||
/**
|
||||
* This will create the mipmap images and populate them with filtered data from base level.
|
||||
@@ -87,6 +91,22 @@ class GLTexture : public Texture {
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLTexture")
|
||||
};
|
||||
|
||||
class GLPixelBuffer : public PixelBuffer {
|
||||
private:
|
||||
GLuint gl_id_ = 0;
|
||||
|
||||
public:
|
||||
GLPixelBuffer(uint size);
|
||||
~GLPixelBuffer();
|
||||
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
int64_t get_native_handle() override;
|
||||
uint get_size() override;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLPixelBuffer")
|
||||
};
|
||||
|
||||
inline GLenum to_gl_internal_format(eGPUTextureFormat format)
|
||||
{
|
||||
/* You can add any of the available type to this list
|
||||
@@ -282,6 +302,8 @@ inline GLenum to_gl(eGPUDataFormat format)
|
||||
return GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return GL_UNSIGNED_INT_10F_11F_11F_REV;
|
||||
case GPU_DATA_HALF_FLOAT:
|
||||
return GL_HALF_FLOAT;
|
||||
default:
|
||||
BLI_assert_msg(0, "Unhandled data format");
|
||||
return GL_FLOAT;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
struct BakeTargets;
|
||||
struct BakePixel;
|
||||
struct Depsgraph;
|
||||
struct GPUContext;
|
||||
struct Main;
|
||||
struct Object;
|
||||
struct Render;
|
||||
@@ -158,9 +159,12 @@ typedef struct RenderEngine {
|
||||
void *update_render_passes_data;
|
||||
|
||||
/* GPU context. */
|
||||
void *gpu_context;
|
||||
void *wm_gpu_context; /* WindowManager GPU context -> GHOSTContext. */
|
||||
ThreadMutex gpu_context_mutex;
|
||||
bool use_drw_render_context;
|
||||
struct GPUContext *gpu_context;
|
||||
/* Whether to restore DRWState after RenderEngine display pass. */
|
||||
bool gpu_restore_context;
|
||||
} RenderEngine;
|
||||
|
||||
RenderEngine *RE_engine_create(RenderEngineType *type);
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include "DEG_depsgraph_debug.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "GPU_context.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
@@ -1285,45 +1287,69 @@ bool RE_engine_gpu_context_create(RenderEngine *engine)
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
|
||||
const bool drw_state = DRW_opengl_context_release();
|
||||
engine->gpu_context = WM_opengl_context_create();
|
||||
engine->wm_gpu_context = WM_opengl_context_create();
|
||||
|
||||
/* On Windows an old context is restored after creation, and subsequent release of context
|
||||
* generates a Win32 error. Harmless for users, but annoying to have possible misleading
|
||||
* error prints in the console. */
|
||||
#ifndef _WIN32
|
||||
if (engine->gpu_context) {
|
||||
WM_opengl_context_release(engine->gpu_context);
|
||||
if (engine->wm_gpu_context) {
|
||||
/* Activate new OpenGL Context for GPUContext creation. */
|
||||
WM_opengl_context_activate(engine->wm_gpu_context);
|
||||
/* Requires GPUContext for usage of GPU Module for displaying results. */
|
||||
engine->gpu_context = GPU_context_create(nullptr, engine->wm_gpu_context);
|
||||
GPU_context_active_set(nullptr);
|
||||
/* Deactivate newly created OpenGL Context, as it is not needed until
|
||||
* `RE_engine_gpu_context_enable` is called. */
|
||||
WM_opengl_context_release(engine->wm_gpu_context);
|
||||
}
|
||||
else {
|
||||
engine->gpu_context = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
DRW_opengl_context_activate(drw_state);
|
||||
|
||||
return engine->gpu_context != nullptr;
|
||||
return engine->wm_gpu_context != nullptr;
|
||||
}
|
||||
|
||||
void RE_engine_gpu_context_destroy(RenderEngine *engine)
|
||||
{
|
||||
if (!engine->gpu_context) {
|
||||
if (!engine->wm_gpu_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool drw_state = DRW_opengl_context_release();
|
||||
|
||||
WM_opengl_context_activate(engine->gpu_context);
|
||||
WM_opengl_context_dispose(engine->gpu_context);
|
||||
WM_opengl_context_activate(engine->wm_gpu_context);
|
||||
if (engine->gpu_context) {
|
||||
GPUContext *restore_context = GPU_context_active_get();
|
||||
GPU_context_active_set(engine->gpu_context);
|
||||
GPU_context_discard(engine->gpu_context);
|
||||
if (restore_context != engine->gpu_context) {
|
||||
GPU_context_active_set(restore_context);
|
||||
}
|
||||
engine->gpu_context = nullptr;
|
||||
}
|
||||
WM_opengl_context_dispose(engine->wm_gpu_context);
|
||||
|
||||
DRW_opengl_context_activate(drw_state);
|
||||
}
|
||||
|
||||
bool RE_engine_gpu_context_enable(RenderEngine *engine)
|
||||
{
|
||||
engine->gpu_restore_context = false;
|
||||
if (engine->use_drw_render_context) {
|
||||
DRW_render_context_enable(engine->re);
|
||||
return true;
|
||||
}
|
||||
if (engine->gpu_context) {
|
||||
if (engine->wm_gpu_context) {
|
||||
BLI_mutex_lock(&engine->gpu_context_mutex);
|
||||
WM_opengl_context_activate(engine->gpu_context);
|
||||
/* If a previous OpenGL/GPUContext was active (DST.gpu_context), we should later restore this
|
||||
* when disabling the RenderEngine context. */
|
||||
engine->gpu_restore_context = DRW_opengl_context_release();
|
||||
|
||||
/* Activate RenderEngine OpenGL and GPU Context. */
|
||||
WM_opengl_context_activate(engine->wm_gpu_context);
|
||||
if (engine->gpu_context) {
|
||||
GPU_context_active_set(engine->gpu_context);
|
||||
GPU_render_begin();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1335,8 +1361,14 @@ void RE_engine_gpu_context_disable(RenderEngine *engine)
|
||||
DRW_render_context_disable(engine->re);
|
||||
}
|
||||
else {
|
||||
if (engine->gpu_context) {
|
||||
WM_opengl_context_release(engine->gpu_context);
|
||||
if (engine->wm_gpu_context) {
|
||||
if (engine->gpu_context) {
|
||||
GPU_render_end();
|
||||
GPU_context_active_set(nullptr);
|
||||
}
|
||||
WM_opengl_context_release(engine->wm_gpu_context);
|
||||
/* Restore DRW state context if previously active. */
|
||||
DRW_opengl_context_activate(engine->gpu_restore_context);
|
||||
BLI_mutex_unlock(&engine->gpu_context_mutex);
|
||||
}
|
||||
}
|
||||
@@ -1348,7 +1380,7 @@ void RE_engine_gpu_context_lock(RenderEngine *engine)
|
||||
/* Locking already handled by the draw manager. */
|
||||
}
|
||||
else {
|
||||
if (engine->gpu_context) {
|
||||
if (engine->wm_gpu_context) {
|
||||
BLI_mutex_lock(&engine->gpu_context_mutex);
|
||||
}
|
||||
}
|
||||
@@ -1360,7 +1392,7 @@ void RE_engine_gpu_context_unlock(RenderEngine *engine)
|
||||
/* Locking already handled by the draw manager. */
|
||||
}
|
||||
else {
|
||||
if (engine->gpu_context) {
|
||||
if (engine->wm_gpu_context) {
|
||||
BLI_mutex_unlock(&engine->gpu_context_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user