Merge branch 'blender-v3.1-release'

This commit is contained in:
Brecht Van Lommel
2022-02-11 14:55:42 +01:00
4 changed files with 155 additions and 63 deletions

View File

@@ -480,26 +480,12 @@ class DrawTile {
return false; return false;
} }
if (!gl_vertex_buffer) {
glGenBuffers(1, &gl_vertex_buffer);
if (!gl_vertex_buffer) {
LOG(ERROR) << "Error allocating tile VBO.";
gl_resources_destroy();
return false;
}
}
return true; return true;
} }
void gl_resources_destroy() void gl_resources_destroy()
{ {
texture.gl_resources_destroy(); texture.gl_resources_destroy();
if (gl_vertex_buffer) {
glDeleteBuffers(1, &gl_vertex_buffer);
gl_vertex_buffer = 0;
}
} }
inline bool ready_to_draw() const inline bool ready_to_draw() const
@@ -512,9 +498,6 @@ class DrawTile {
/* Display parameters the texture of this tile has been updated for. */ /* Display parameters the texture of this tile has been updated for. */
BlenderDisplayDriver::Params params; BlenderDisplayDriver::Params params;
/* OpenGL resources needed for drawing. */
uint gl_vertex_buffer = 0;
}; };
class DrawTileAndPBO { class DrawTileAndPBO {
@@ -560,6 +543,30 @@ struct BlenderDisplayDriver::Tiles {
tiles.clear(); tiles.clear();
} }
} finished_tiles; } 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, BL::Scene &b_scene) BlenderDisplayDriver::BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene)
@@ -626,6 +633,12 @@ bool BlenderDisplayDriver::update_begin(const Params &params,
need_clear_ = false; need_clear_ = false;
} }
if (!tiles_->gl_resources_ensure()) {
tiles_->gl_resources_destroy();
gl_context_disable();
return false;
}
if (!tiles_->current_tile.gl_resources_ensure()) { if (!tiles_->current_tile.gl_resources_ensure()) {
tiles_->current_tile.gl_resources_destroy(); tiles_->current_tile.gl_resources_destroy();
gl_context_disable(); gl_context_disable();
@@ -825,7 +838,8 @@ static void vertex_buffer_update(const DisplayDriver::Params &params)
static void draw_tile(const float2 &zoom, static void draw_tile(const float2 &zoom,
const int texcoord_attribute, const int texcoord_attribute,
const int position_attribute, const int position_attribute,
const DrawTile &draw_tile) const DrawTile &draw_tile,
const uint gl_vertex_buffer)
{ {
if (!draw_tile.ready_to_draw()) { if (!draw_tile.ready_to_draw()) {
return; return;
@@ -834,9 +848,9 @@ static void draw_tile(const float2 &zoom,
const GLTexture &texture = draw_tile.texture; const GLTexture &texture = draw_tile.texture;
DCHECK_NE(texture.gl_id, 0); DCHECK_NE(texture.gl_id, 0);
DCHECK_NE(draw_tile.gl_vertex_buffer, 0); DCHECK_NE(gl_vertex_buffer, 0);
glBindBuffer(GL_ARRAY_BUFFER, draw_tile.gl_vertex_buffer); glBindBuffer(GL_ARRAY_BUFFER, gl_vertex_buffer);
/* Draw at the parameters for which the texture has been updated for. This allows to always draw /* 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 * texture during bordered-rendered camera view without flickering. The validness of the display
@@ -956,10 +970,14 @@ void BlenderDisplayDriver::draw(const Params &params)
glEnableVertexAttribArray(texcoord_attribute); glEnableVertexAttribArray(texcoord_attribute);
glEnableVertexAttribArray(position_attribute); glEnableVertexAttribArray(position_attribute);
draw_tile(zoom_, texcoord_attribute, position_attribute, tiles_->current_tile.tile); draw_tile(zoom_,
texcoord_attribute,
position_attribute,
tiles_->current_tile.tile,
tiles_->gl_vertex_buffer);
for (const DrawTile &tile : tiles_->finished_tiles.tiles) { for (const DrawTile &tile : tiles_->finished_tiles.tiles) {
draw_tile(zoom_, texcoord_attribute, position_attribute, tile); draw_tile(zoom_, texcoord_attribute, position_attribute, tile, tiles_->gl_vertex_buffer);
} }
display_shader_->unbind(); display_shader_->unbind();
@@ -1062,6 +1080,7 @@ void BlenderDisplayDriver::gl_resources_destroy()
tiles_->current_tile.gl_resources_destroy(); tiles_->current_tile.gl_resources_destroy();
tiles_->finished_tiles.gl_resources_destroy_and_clear(); tiles_->finished_tiles.gl_resources_destroy_and_clear();
tiles_->gl_resources_destroy();
gl_context_disable(); gl_context_disable();

View File

@@ -49,12 +49,9 @@ Session::Session(const SessionParams &params_, const SceneParams &scene_params)
{ {
TaskScheduler::init(params.threads); TaskScheduler::init(params.threads);
session_thread_ = nullptr;
delayed_reset_.do_reset = false; delayed_reset_.do_reset = false;
pause_ = false; pause_ = false;
cancel_ = false;
new_work_added_ = false; new_work_added_ = false;
device = Device::create(params.device, stats, profiler); device = Device::create(params.device, stats, profiler);
@@ -73,48 +70,79 @@ Session::Session(const SessionParams &params_, const SceneParams &scene_params)
} }
full_buffer_written_cb(filename); full_buffer_written_cb(filename);
}; };
/* Create session thread. */
session_thread_ = new thread(function_bind(&Session::thread_run, this));
} }
Session::~Session() Session::~Session()
{ {
/* Cancel any ongoing render operation. */
cancel(); cancel();
/* Make sure path tracer is destroyed before the device. This is needed because destruction might /* Signal session thread to end. */
* need to access device for device memory free. */ {
/* TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the thread_scoped_lock session_thread_lock(session_thread_mutex_);
session_thread_state_ = SESSION_THREAD_END;
}
session_thread_cond_.notify_all();
/* Destroy session thread. */
session_thread_->join();
delete session_thread_;
/* Destroy path tracer, before the device. This is needed because destruction might need to
* access device for device memory free.
* TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the
* pre-defined order. */ * pre-defined order. */
path_trace_.reset(); path_trace_.reset();
/* Destroy scene and device. */
delete scene; delete scene;
delete device; delete device;
/* Stop task scheduler. */
TaskScheduler::exit(); TaskScheduler::exit();
} }
void Session::start() void Session::start()
{ {
if (!session_thread_) { {
session_thread_ = new thread(function_bind(&Session::run, this)); /* Signal session thread to start rendering. */
thread_scoped_lock session_thread_lock(session_thread_mutex_);
assert(session_thread_state_ == SESSION_THREAD_WAIT);
session_thread_state_ = SESSION_THREAD_RENDER;
} }
session_thread_cond_.notify_all();
} }
void Session::cancel(bool quick) void Session::cancel(bool quick)
{ {
if (quick && path_trace_) { /* Check if session thread is rendering. */
path_trace_->cancel(); bool rendering;
{
thread_scoped_lock session_thread_lock(session_thread_mutex_);
rendering = (session_thread_state_ == SESSION_THREAD_RENDER);
} }
if (session_thread_) { if (rendering) {
/* wait for session thread to end */ /* Cancel path trace operations. */
if (quick && path_trace_) {
path_trace_->cancel();
}
/* Cancel other operations. */
progress.set_cancel("Exiting"); progress.set_cancel("Exiting");
/* Signal unpause in case the render was paused. */
{ {
thread_scoped_lock pause_lock(pause_mutex_); thread_scoped_lock pause_lock(pause_mutex_);
pause_ = false; pause_ = false;
cancel_ = true;
} }
pause_cond_.notify_all(); pause_cond_.notify_all();
/* Wait for render thread to be cancelled or finished. */
wait(); wait();
} }
} }
@@ -192,11 +220,46 @@ void Session::run_main_render_loop()
break; break;
} }
} }
path_trace_->flush_display();
} }
void Session::run() void Session::thread_run()
{
while (true) {
{
thread_scoped_lock session_thread_lock(session_thread_mutex_);
if (session_thread_state_ == SESSION_THREAD_WAIT) {
/* Continue waiting for any signal from the main thread. */
session_thread_cond_.wait(session_thread_lock);
continue;
}
else if (session_thread_state_ == SESSION_THREAD_END) {
/* End thread immediately. */
break;
}
}
/* Execute a render. */
thread_render();
/* Go back from rendering to waiting. */
{
thread_scoped_lock session_thread_lock(session_thread_mutex_);
if (session_thread_state_ == SESSION_THREAD_RENDER) {
session_thread_state_ = SESSION_THREAD_WAIT;
}
}
session_thread_cond_.notify_all();
}
/* Flush any remaining operations and destroy display driver here. This ensure
* graphics API resources are created and destroyed all in the session thread,
* which can avoid problems contexts and multiple threads. */
path_trace_->flush_display();
path_trace_->set_display_driver(nullptr);
}
void Session::thread_render()
{ {
if (params.use_profiling && (params.device.type == DEVICE_CPU)) { if (params.use_profiling && (params.device.type == DEVICE_CPU)) {
profiler.start(); profiler.start();
@@ -338,9 +401,9 @@ bool Session::run_wait_for_work(const RenderWork &render_work)
const bool no_work = !render_work; const bool no_work = !render_work;
update_status_time(pause_, no_work); update_status_time(pause_, no_work);
/* Only leave the loop when rendering is not paused. But even if the current render is un-paused /* Only leave the loop when rendering is not paused. But even if the current render is
* but there is nothing to render keep waiting until new work is added. */ * un-paused but there is nothing to render keep waiting until new work is added. */
while (!cancel_) { while (!progress.get_cancel()) {
scoped_timer pause_timer; scoped_timer pause_timer;
if (!pause_ && (render_work || new_work_added_ || delayed_reset_.do_reset)) { if (!pause_ && (render_work || new_work_added_ || delayed_reset_.do_reset)) {
@@ -427,7 +490,8 @@ void Session::do_delayed_reset()
tile_manager_.update(buffer_params_, scene); tile_manager_.update(buffer_params_, scene);
/* Update temp directory on reset. /* Update temp directory on reset.
* This potentially allows to finish the existing rendering with a previously configure temporary * This potentially allows to finish the existing rendering with a previously configure
* temporary
* directory in the host software and switch to a new temp directory when new render starts. */ * directory in the host software and switch to a new temp directory when new render starts. */
tile_manager_.set_temp_dir(params.temp_dir); tile_manager_.set_temp_dir(params.temp_dir);
@@ -544,12 +608,14 @@ double Session::get_estimated_remaining_time() const
void Session::wait() void Session::wait()
{ {
if (session_thread_) { /* Wait until session thread either is waiting or ending. */
session_thread_->join(); while (true) {
delete session_thread_; thread_scoped_lock session_thread_lock(session_thread_mutex_);
if (session_thread_state_ != SESSION_THREAD_RENDER) {
break;
}
session_thread_cond_.wait(session_thread_lock);
} }
session_thread_ = nullptr;
} }
bool Session::update_scene(int width, int height) bool Session::update_scene(int width, int height)

View File

@@ -172,7 +172,8 @@ class Session {
BufferParams buffer_params; BufferParams buffer_params;
} delayed_reset_; } delayed_reset_;
void run(); void thread_run();
void thread_render();
/* Update for the new iteration of the main loop in run implementation (run_cpu and run_gpu). /* Update for the new iteration of the main loop in run implementation (run_cpu and run_gpu).
* *
@@ -205,10 +206,19 @@ class Session {
int2 get_effective_tile_size() const; int2 get_effective_tile_size() const;
thread *session_thread_; /* Session thread that performs rendering tasks decoupled from the thread
* controlling the sessions. The thread is created and destroyed along with
* the session. */
thread *session_thread_ = nullptr;
thread_condition_variable session_thread_cond_;
thread_mutex session_thread_mutex_;
enum {
SESSION_THREAD_WAIT,
SESSION_THREAD_RENDER,
SESSION_THREAD_END,
} session_thread_state_ = SESSION_THREAD_WAIT;
bool pause_ = false; bool pause_ = false;
bool cancel_ = false;
bool new_work_added_ = false; bool new_work_added_ = false;
thread_condition_variable pause_cond_; thread_condition_variable pause_cond_;

View File

@@ -683,19 +683,16 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS)
set(_cycles_render_tests bake;${render_tests};osl) set(_cycles_render_tests bake;${render_tests};osl)
foreach(render_test ${_cycles_render_tests}) foreach(render_test ${_cycles_render_tests})
# Enable just one simple test for Metal until more tests are passing. add_python_test(
if((NOT (_cycles_device MATCHES "METAL")) OR (render_test MATCHES "camera")) cycles_${render_test}_${_cycles_device_lower}
add_python_test( ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py
cycles_${render_test}_${_cycles_device_lower} -blender "${TEST_BLENDER_EXE}"
${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py -testdir "${TEST_SRC_DIR}/render/${render_test}"
-blender "${TEST_BLENDER_EXE}" -idiff "${OPENIMAGEIO_IDIFF}"
-testdir "${TEST_SRC_DIR}/render/${render_test}" -outdir "${TEST_OUT_DIR}/cycles"
-idiff "${OPENIMAGEIO_IDIFF}" -device ${_cycles_device}
-outdir "${TEST_OUT_DIR}/cycles" -blacklist ${_cycles_blacklist}
-device ${_cycles_device} )
-blacklist ${_cycles_blacklist}
)
endif()
endforeach() endforeach()
endforeach() endforeach()
endif() endif()