mirror of
https://github.com/Cuteminded/jellyframe.git
synced 2026-10-18 20:00:29 +02:00
99 lines
3.9 KiB
C++
99 lines
3.9 KiB
C++
#include "render_gl.h"
|
|
#include <chrono>
|
|
|
|
int RenderGl::acquire_slot() {
|
|
std::lock_guard<std::mutex> lock(slots_mutex);
|
|
for (size_t index = 0; index < slots.size(); ++index) {
|
|
auto &slot = slots[index];
|
|
if (slot.state == SlotState::Retired && fence_complete(slot.released)) {
|
|
api.delete_fence(slot.released);
|
|
slot.released = nullptr;
|
|
slot.state = SlotState::Free;
|
|
}
|
|
if (slot.state == SlotState::Free) {
|
|
slot.state = SlotState::Rendering;
|
|
return int(index);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool RenderGl::prepare_slot(Slot &slot) {
|
|
uint64_t size = dimensions.load();
|
|
int width = int(size >> 32);
|
|
int height = int(size & 0xffffffff);
|
|
if (slot.texture && slot.width == width && slot.height == height) return true;
|
|
if (slot.framebuffer) api.delete_framebuffers(1, &slot.framebuffer);
|
|
if (slot.texture) api.delete_textures(1, &slot.texture);
|
|
slot.width = width;
|
|
slot.height = height;
|
|
api.gen_textures(1, &slot.texture);
|
|
api.bind_texture(gl::texture_2d, slot.texture);
|
|
api.texture_parameter(gl::texture_2d, gl::texture_min_filter, gl::nearest);
|
|
api.texture_parameter(gl::texture_2d, gl::texture_mag_filter, gl::nearest);
|
|
api.texture_image(gl::texture_2d, 0, gl::rgba8, width, height, 0, gl::rgba, gl::unsigned_byte, nullptr);
|
|
api.bind_texture(gl::texture_2d, 0);
|
|
api.gen_framebuffers(1, &slot.framebuffer);
|
|
api.bind_framebuffer(gl::framebuffer, slot.framebuffer);
|
|
api.attach_texture(gl::framebuffer, gl::color_attachment, gl::texture_2d, slot.texture, 0);
|
|
bool complete = api.check_framebuffer(gl::framebuffer) == gl::framebuffer_complete;
|
|
api.bind_framebuffer(gl::framebuffer, 0);
|
|
if (!complete) render_error = MPV_ERROR_UNSUPPORTED;
|
|
return complete;
|
|
}
|
|
|
|
bool RenderGl::render_slot(mpv_render_context *renderer, Slot &slot) {
|
|
if (!prepare_slot(slot)) return false;
|
|
mpv_opengl_fbo target{int(slot.framebuffer), slot.width, slot.height, int(gl::rgba8)};
|
|
int flip = 0;
|
|
int block = 1;
|
|
mpv_render_param params[] = {
|
|
{MPV_RENDER_PARAM_OPENGL_FBO, &target},
|
|
{MPV_RENDER_PARAM_FLIP_Y, &flip},
|
|
{MPV_RENDER_PARAM_BLOCK_FOR_TARGET_TIME, &block},
|
|
{MPV_RENDER_PARAM_INVALID, nullptr}
|
|
};
|
|
auto started = std::chrono::steady_clock::now();
|
|
int error = mpv_render_context_render(renderer, params);
|
|
render_time_us = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - started).count();
|
|
if (error < 0) { render_error = error; return false; }
|
|
void *ready = api.fence(gl::sync_complete, 0);
|
|
api.flush();
|
|
if (!ready) { render_error = MPV_ERROR_GENERIC; return false; }
|
|
{
|
|
std::lock_guard<std::mutex> lock(slots_mutex);
|
|
slot.ready = ready;
|
|
slot.sequence = ++next_sequence;
|
|
slot.state = SlotState::Ready;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void RenderGl::render_loop(mpv_render_context *renderer) {
|
|
bool pending = false;
|
|
while (!stopping && render_error >= 0) {
|
|
if (dirty.exchange(false)) {
|
|
if (mpv_render_context_update(renderer) & MPV_RENDER_UPDATE_FRAME) pending = true;
|
|
}
|
|
int selected = pending && dimensions.load() ? acquire_slot() : -1;
|
|
if (selected >= 0) {
|
|
if (!render_slot(renderer, slots[selected])) break;
|
|
pending = false;
|
|
continue;
|
|
}
|
|
std::unique_lock<std::mutex> lock(wake_mutex);
|
|
wake.wait_for(lock, std::chrono::milliseconds(pending ? 2 : 20), [this] { return stopping || dirty; });
|
|
}
|
|
}
|
|
|
|
void RenderGl::destroy_slots() {
|
|
std::lock_guard<std::mutex> lock(slots_mutex);
|
|
for (auto &slot : slots) {
|
|
if (slot.ready) api.delete_fence(slot.ready);
|
|
if (slot.released) api.delete_fence(slot.released);
|
|
if (slot.framebuffer) api.delete_framebuffers(1, &slot.framebuffer);
|
|
if (slot.texture) api.delete_textures(1, &slot.texture);
|
|
slot = Slot();
|
|
}
|
|
}
|