From d2a6e6303d7daaa88560392f8140f774a8d47eb5 Mon Sep 17 00:00:00 2001 From: ffreyer Date: Fri, 14 Oct 2022 19:36:56 +0200 Subject: [PATCH 01/11] prototyping --- GLMakie/src/GLAbstraction/AbstractGPUArray.jl | 1 + GLMakie/src/GLAbstraction/GLRender.jl | 2 ++ GLMakie/src/GLAbstraction/GLRenderObject.jl | 5 ++++- GLMakie/src/GLAbstraction/GLTypes.jl | 18 ++++++++++++++-- GLMakie/src/GLAbstraction/GLUniforms.jl | 1 + GLMakie/src/screen.jl | 21 +++++++++++++++++-- 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl index 0d87dbbe643..71a1a166d7d 100644 --- a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl +++ b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl @@ -192,6 +192,7 @@ max_dim(t) = error("max_dim not implemented for: $(typeof(t)). This happen function (::Type{T})(x::Observable; kw...) where T <: GPUArray + @info "contructor" gpu_mem = T(x[]; kw...) on(x-> update!(gpu_mem, x), x) gpu_mem diff --git a/GLMakie/src/GLAbstraction/GLRender.jl b/GLMakie/src/GLAbstraction/GLRender.jl index 8e1987866de..a0ff7119865 100644 --- a/GLMakie/src/GLAbstraction/GLRender.jl +++ b/GLMakie/src/GLAbstraction/GLRender.jl @@ -56,6 +56,8 @@ a lot of objects. """ function render(renderobject::RenderObject, vertexarray=renderobject.vertexarray) if Bool(to_value(renderobject.uniforms[:visible])) + renderobject.requires_update = false + renderobject.prerenderfunction() program = vertexarray.program glUseProgram(program.id) diff --git a/GLMakie/src/GLAbstraction/GLRenderObject.jl b/GLMakie/src/GLAbstraction/GLRenderObject.jl index fe644f5c586..56f8e50fa37 100644 --- a/GLMakie/src/GLAbstraction/GLRenderObject.jl +++ b/GLMakie/src/GLAbstraction/GLRenderObject.jl @@ -3,7 +3,10 @@ function Base.show(io::IO, obj::RenderObject) end Base.getindex(obj::RenderObject, symbol::Symbol) = obj.uniforms[symbol] -Base.setindex!(obj::RenderObject, value, symbol::Symbol) = obj.uniforms[symbol] = value +function Base.setindex!(obj::RenderObject, value, symbol::Symbol) + @info "Adding uniform $symbol" + obj.uniforms[symbol] = value +end Base.getindex(obj::RenderObject, symbol::Symbol, x::Function) = getindex(obj, Val(symbol), x) Base.getindex(obj::RenderObject, ::Val{:prerender}, x::Function) = obj.prerenderfunctions[x] diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index e888216dfd0..70eebaf312b 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -289,6 +289,7 @@ mutable struct RenderObject{Pre} prerenderfunction::Pre postrenderfunction id::UInt32 + requires_update::Bool function RenderObject{Pre}( context, uniforms::Dict{Symbol,Any}, observables::Vector{Observable}, @@ -303,12 +304,25 @@ mutable struct RenderObject{Pre} # But with this implementation, the fxaa flag can't be changed, # and since this is a UUID, it shouldn't matter id = pack_bool(RENDER_OBJECT_ID_COUNTER[], fxaa) - new( + @info "RenderObject $id with $(length(uniforms)) uniforms" + robj = new( context, uniforms, observables, vertexarray, prerenderfunctions, postrenderfunctions, - id + id, true ) + # Checking each robj.requires_update after polling events seems like + # a better idea than having an observable chain... + for (key, maybe_obs) in uniforms + if maybe_obs isa Observable + obsfunc = on(maybe_obs) do _ + @info "Requesting update of $id $key" + robj.requires_update = true + end + end + end + + return robj end end diff --git a/GLMakie/src/GLAbstraction/GLUniforms.jl b/GLMakie/src/GLAbstraction/GLUniforms.jl index 17f83cc7ec8..8f3f73924ef 100644 --- a/GLMakie/src/GLAbstraction/GLUniforms.jl +++ b/GLMakie/src/GLAbstraction/GLUniforms.jl @@ -248,6 +248,7 @@ gl_convert(::Type{<: GPUArray}, a::Observable{<: StaticVector}) = gl_convert(a) function gl_convert(::Type{T}, a::Observable{<: AbstractArray{X, N}}; kw_args...) where {T <: GPUArray, X, N} TGL = gl_promote(X) s = (X == TGL) ? a : lift(x-> convert(Array{TGL, N}, x), a) + @info TGL T(s; kw_args...) end diff --git a/GLMakie/src/screen.jl b/GLMakie/src/screen.jl index 65f00200d41..8792650bd9d 100644 --- a/GLMakie/src/screen.jl +++ b/GLMakie/src/screen.jl @@ -578,6 +578,7 @@ function renderloop_running(screen::Screen) end function start_renderloop!(screen::Screen) + @info "start_renderloop" if renderloop_running(screen) screen.config.pause_renderloop = false return @@ -642,7 +643,20 @@ function vsynced_renderloop(screen) end end +function requires_update(screen::Screen) + for (_, _, robj) in screen.renderlist + visible = Bool(to_value(get(robj.uniforms, :visible, true))) + if visible && robj.requires_update + return true + end + end + return false +end + +const UPDATES = Ref(0) + function fps_renderloop(screen::Screen) + UPDATES[] = 0 while isopen(screen) && !screen.stop_renderloop if screen.config.pause_renderloop pollevents(screen); sleep(0.1) @@ -651,8 +665,11 @@ function fps_renderloop(screen::Screen) time_per_frame = 1.0 / screen.config.framerate t = time_ns() pollevents(screen) # GLFW poll - render_frame(screen) - GLFW.SwapBuffers(to_native(screen)) + if requires_update(screen) + UPDATES[] += 1 + render_frame(screen) + GLFW.SwapBuffers(to_native(screen)) + end t_elapsed = (time_ns() - t) / 1e9 diff = time_per_frame - t_elapsed if diff > 0.001 # can't sleep less than 0.001 From 8b5131939731d1401a3bcf9e670534c9a4ce012b Mon Sep 17 00:00:00 2001 From: ffreyer Date: Fri, 14 Oct 2022 20:16:29 +0200 Subject: [PATCH 02/11] track vertexarray buffers --- GLMakie/src/GLAbstraction/AbstractGPUArray.jl | 6 +++++- GLMakie/src/GLAbstraction/GLBuffer.jl | 4 +++- GLMakie/src/GLAbstraction/GLTypes.jl | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl index 71a1a166d7d..2e3cd0e5780 100644 --- a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl +++ b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl @@ -194,7 +194,11 @@ max_dim(t) = error("max_dim not implemented for: $(typeof(t)). This happen function (::Type{T})(x::Observable; kw...) where T <: GPUArray @info "contructor" gpu_mem = T(x[]; kw...) - on(x-> update!(gpu_mem, x), x) + on(x) do x + @info "Update $T" + gpu_mem.requires_update[] = true + update!(gpu_mem, x) + end gpu_mem end diff --git a/GLMakie/src/GLAbstraction/GLBuffer.jl b/GLMakie/src/GLAbstraction/GLBuffer.jl index 46d2ffbdb6e..f3d798b8ef5 100644 --- a/GLMakie/src/GLAbstraction/GLBuffer.jl +++ b/GLMakie/src/GLAbstraction/GLBuffer.jl @@ -4,6 +4,8 @@ mutable struct GLBuffer{T} <: GPUArray{T, 1} buffertype ::GLenum usage ::GLenum context ::GLContext + # TODO maybe also delay upload to when render happens? + requires_update::Observable{Bool} function GLBuffer{T}(ptr::Ptr{T}, buff_length::Int, buffertype::GLenum, usage::GLenum) where T id = glGenBuffers() @@ -13,7 +15,7 @@ mutable struct GLBuffer{T} <: GPUArray{T, 1} glBufferData(buffertype, buff_length * sizeof(T), ptr, usage) glBindBuffer(buffertype, 0) - obj = new(id, (buff_length,), buffertype, usage, current_context()) + obj = new(id, (buff_length,), buffertype, usage, current_context(), Observable(true)) finalizer(free, obj) obj end diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index 70eebaf312b..9744dc399d7 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -174,9 +174,18 @@ mutable struct GLVertexArray{T} buffers::Dict{String,GLBuffer} indices::T context::GLContext + requires_update::Observable{Bool} function GLVertexArray{T}(program, id, bufferlength, buffers, indices) where T - new(program, id, bufferlength, buffers, indices, current_context()) + va = new(program, id, bufferlength, buffers, indices, current_context(), true) + for (name, buffer) in buffers + on(buffer.requires_update) do _ # only triggers true anyway + @info "VertexArray buffer $name update" + va.requires_update[] = true + end + end + + return va end end @@ -315,12 +324,13 @@ mutable struct RenderObject{Pre} # a better idea than having an observable chain... for (key, maybe_obs) in uniforms if maybe_obs isa Observable - obsfunc = on(maybe_obs) do _ + on(maybe_obs) do _ @info "Requesting update of $id $key" robj.requires_update = true end end end + on(_ -> robj.requires_update = true, vertexarray.requires_update) return robj end From bd0f73d0d1d85f79b6eb5febac024809591e333b Mon Sep 17 00:00:00 2001 From: ffreyer Date: Sat, 15 Oct 2022 13:00:09 +0200 Subject: [PATCH 03/11] track Texture updates --- GLMakie/src/GLAbstraction/GLTexture.jl | 11 ++++++++++- GLMakie/src/GLAbstraction/GLTypes.jl | 11 ++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/GLMakie/src/GLAbstraction/GLTexture.jl b/GLMakie/src/GLAbstraction/GLTexture.jl index 51521d9fdf6..11d8b5a6a28 100644 --- a/GLMakie/src/GLAbstraction/GLTexture.jl +++ b/GLMakie/src/GLAbstraction/GLTexture.jl @@ -17,6 +17,7 @@ mutable struct Texture{T <: GLArrayEltypes, NDIM} <: OpenglTexture{T, NDIM} parameters ::TextureParameters{NDIM} size ::NTuple{NDIM, Int} context ::GLContext + requires_update ::Observable{Bool} function Texture{T, NDIM}( id ::GLuint, texturetype ::GLenum, @@ -34,7 +35,8 @@ mutable struct Texture{T <: GLArrayEltypes, NDIM} <: OpenglTexture{T, NDIM} format, parameters, size, - current_context() + current_context(), + Observable(true) ) finalizer(free, tex) tex @@ -45,6 +47,13 @@ end mutable struct TextureBuffer{T <: GLArrayEltypes} <: OpenglTexture{T, 1} texture::Texture{T, 1} buffer::GLBuffer{T} + requires_update::Observable{Bool} + + function TextureBuffer(texture::Texture{T, 1}, buffer::GLBuffer{T}) where T + # do we need to listen to texture changes or is buffer enough? + x = map((_, _) -> true, buffer.requires_update, texture.requires_update) + new{T}(texture, buffer, x) + end end Base.size(t::TextureBuffer) = size(t.buffer) Base.size(t::TextureBuffer, i::Integer) = size(t.buffer, i) diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index 9744dc399d7..e363b90f14a 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -322,9 +322,14 @@ mutable struct RenderObject{Pre} ) # Checking each robj.requires_update after polling events seems like # a better idea than having an observable chain... - for (key, maybe_obs) in uniforms - if maybe_obs isa Observable - on(maybe_obs) do _ + for (key, uniform) in uniforms + if uniform isa Observable + on(uniform) do _ + @info "Requesting update of $id $key" + robj.requires_update = true + end + elseif uniform isa Union{Texture, TextureBuffer, GLBuffer} + on(uniform.requires_update) do _ @info "Requesting update of $id $key" robj.requires_update = true end From 9017d3a71a76574ce4841c9fb90c3894e8e90f50 Mon Sep 17 00:00:00 2001 From: ffreyer Date: Sat, 15 Oct 2022 13:26:28 +0200 Subject: [PATCH 04/11] remove prints --- GLMakie/src/GLAbstraction/AbstractGPUArray.jl | 6 ++++-- GLMakie/src/GLAbstraction/GLRenderObject.jl | 5 +---- GLMakie/src/GLAbstraction/GLTypes.jl | 4 ---- GLMakie/src/GLAbstraction/GLUniforms.jl | 1 - GLMakie/src/screen.jl | 1 - 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl index 2e3cd0e5780..fe4019088c3 100644 --- a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl +++ b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl @@ -88,6 +88,10 @@ mutable struct GPUVector{T} <: GPUArray{T, 1} buffer size real_length + + function GPUVector{T}(buffer, size, real_length) where T + new{T}(buffer, size, real_length) + end end GPUVector(x::GPUArray) = GPUVector{eltype(x)}(x, size(x), length(x)) @@ -192,10 +196,8 @@ max_dim(t) = error("max_dim not implemented for: $(typeof(t)). This happen function (::Type{T})(x::Observable; kw...) where T <: GPUArray - @info "contructor" gpu_mem = T(x[]; kw...) on(x) do x - @info "Update $T" gpu_mem.requires_update[] = true update!(gpu_mem, x) end diff --git a/GLMakie/src/GLAbstraction/GLRenderObject.jl b/GLMakie/src/GLAbstraction/GLRenderObject.jl index 56f8e50fa37..fe644f5c586 100644 --- a/GLMakie/src/GLAbstraction/GLRenderObject.jl +++ b/GLMakie/src/GLAbstraction/GLRenderObject.jl @@ -3,10 +3,7 @@ function Base.show(io::IO, obj::RenderObject) end Base.getindex(obj::RenderObject, symbol::Symbol) = obj.uniforms[symbol] -function Base.setindex!(obj::RenderObject, value, symbol::Symbol) - @info "Adding uniform $symbol" - obj.uniforms[symbol] = value -end +Base.setindex!(obj::RenderObject, value, symbol::Symbol) = obj.uniforms[symbol] = value Base.getindex(obj::RenderObject, symbol::Symbol, x::Function) = getindex(obj, Val(symbol), x) Base.getindex(obj::RenderObject, ::Val{:prerender}, x::Function) = obj.prerenderfunctions[x] diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index e363b90f14a..d28cac15ec0 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -180,7 +180,6 @@ mutable struct GLVertexArray{T} va = new(program, id, bufferlength, buffers, indices, current_context(), true) for (name, buffer) in buffers on(buffer.requires_update) do _ # only triggers true anyway - @info "VertexArray buffer $name update" va.requires_update[] = true end end @@ -313,7 +312,6 @@ mutable struct RenderObject{Pre} # But with this implementation, the fxaa flag can't be changed, # and since this is a UUID, it shouldn't matter id = pack_bool(RENDER_OBJECT_ID_COUNTER[], fxaa) - @info "RenderObject $id with $(length(uniforms)) uniforms" robj = new( context, uniforms, observables, vertexarray, @@ -325,12 +323,10 @@ mutable struct RenderObject{Pre} for (key, uniform) in uniforms if uniform isa Observable on(uniform) do _ - @info "Requesting update of $id $key" robj.requires_update = true end elseif uniform isa Union{Texture, TextureBuffer, GLBuffer} on(uniform.requires_update) do _ - @info "Requesting update of $id $key" robj.requires_update = true end end diff --git a/GLMakie/src/GLAbstraction/GLUniforms.jl b/GLMakie/src/GLAbstraction/GLUniforms.jl index 8f3f73924ef..17f83cc7ec8 100644 --- a/GLMakie/src/GLAbstraction/GLUniforms.jl +++ b/GLMakie/src/GLAbstraction/GLUniforms.jl @@ -248,7 +248,6 @@ gl_convert(::Type{<: GPUArray}, a::Observable{<: StaticVector}) = gl_convert(a) function gl_convert(::Type{T}, a::Observable{<: AbstractArray{X, N}}; kw_args...) where {T <: GPUArray, X, N} TGL = gl_promote(X) s = (X == TGL) ? a : lift(x-> convert(Array{TGL, N}, x), a) - @info TGL T(s; kw_args...) end diff --git a/GLMakie/src/screen.jl b/GLMakie/src/screen.jl index 8792650bd9d..3c945208e2d 100644 --- a/GLMakie/src/screen.jl +++ b/GLMakie/src/screen.jl @@ -578,7 +578,6 @@ function renderloop_running(screen::Screen) end function start_renderloop!(screen::Screen) - @info "start_renderloop" if renderloop_running(screen) screen.config.pause_renderloop = false return From fa2d1795b2cda079ab17266205f4d09d460a1ae7 Mon Sep 17 00:00:00 2001 From: ffreyer Date: Sat, 15 Oct 2022 15:07:13 +0200 Subject: [PATCH 05/11] add new renderloop --- GLMakie/src/screen.jl | 43 +++++++++++++++++++++++++++++++++++-------- src/theming.jl | 1 + 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/GLMakie/src/screen.jl b/GLMakie/src/screen.jl index 3c945208e2d..a080b5be0dd 100644 --- a/GLMakie/src/screen.jl +++ b/GLMakie/src/screen.jl @@ -17,6 +17,7 @@ function renderloop end * `pause_renderloop = false`: creates a screen with paused renderlooop. Can be started with `GLMakie.start_renderloop!(screen)` or paused again with `GLMakie.pause_renderloop!(screen)`. * `vsync = false`: enables vsync for the window. +* `render_on_demand = true`: renders the scene only if something has changed in it. * `framerate = 30.0`: sets the currently rendered frames per second. ## GLFW window attributes @@ -40,6 +41,7 @@ mutable struct ScreenConfig renderloop::Function pause_renderloop::Bool vsync::Bool + render_on_demand::Bool framerate::Float64 # GLFW window attributes @@ -62,6 +64,7 @@ mutable struct ScreenConfig renderloop::Union{Makie.Automatic, Function}, pause_renderloop::Bool, vsync::Bool, + render_on_demand::Bool, framerate::Number, # GLFW window attributes float::Bool, @@ -82,6 +85,7 @@ mutable struct ScreenConfig renderloop isa Makie.Automatic ? GLMakie.renderloop : renderloop, pause_renderloop, vsync, + render_on_demand, framerate, # GLFW window attributes float, @@ -642,6 +646,28 @@ function vsynced_renderloop(screen) end end +function fps_renderloop(screen::Screen) + while isopen(screen) && !screen.stop_renderloop + if screen.config.pause_renderloop + pollevents(screen); sleep(0.1) + continue + end + time_per_frame = 1.0 / screen.config.framerate + t = time_ns() + pollevents(screen) # GLFW poll + render_frame(screen) + GLFW.SwapBuffers(to_native(screen)) + t_elapsed = (time_ns() - t) / 1e9 + diff = time_per_frame - t_elapsed + if diff > 0.001 # can't sleep less than 0.001 + sleep(diff) + else # if we don't sleep, we still need to yield explicitely to other tasks + yield() + end + end +end + + function requires_update(screen::Screen) for (_, _, robj) in screen.renderlist visible = Bool(to_value(get(robj.uniforms, :visible, true))) @@ -654,21 +680,19 @@ end const UPDATES = Ref(0) -function fps_renderloop(screen::Screen) +function on_demand_renderloop(screen::Screen) UPDATES[] = 0 while isopen(screen) && !screen.stop_renderloop - if screen.config.pause_renderloop - pollevents(screen); sleep(0.1) - continue - end - time_per_frame = 1.0 / screen.config.framerate t = time_ns() + time_per_frame = 1.0 / screen.config.framerate pollevents(screen) # GLFW poll - if requires_update(screen) + + if !screen.config.pause_renderloop && requires_update(screen) UPDATES[] += 1 render_frame(screen) GLFW.SwapBuffers(to_native(screen)) end + t_elapsed = (time_ns() - t) / 1e9 diff = time_per_frame - t_elapsed if diff > 0.001 # can't sleep less than 0.001 @@ -682,7 +706,10 @@ end function renderloop(screen) isopen(screen) || error("Screen most be open to run renderloop!") try - if screen.config.vsync + if screen.config.render_on_demand + GLFW.SwapInterval(0) + on_demand_renderloop(screen) + elseif screen.config.vsync GLFW.SwapInterval(1) vsynced_renderloop(screen) else diff --git a/src/theming.jl b/src/theming.jl index ea27dee7611..7f90b905e93 100644 --- a/src/theming.jl +++ b/src/theming.jl @@ -106,6 +106,7 @@ const minimal_default = Attributes( renderloop = automatic, pause_renderloop = false, vsync = false, + render_on_demand = true, framerate = 30.0, # GLFW window attributes From f1b70d803bb4ed0f35c2c6ece83064012884f4ab Mon Sep 17 00:00:00 2001 From: ffreyer Date: Sat, 15 Oct 2022 15:14:48 +0200 Subject: [PATCH 06/11] cleanup --- GLMakie/src/GLAbstraction/AbstractGPUArray.jl | 4 ---- GLMakie/src/GLAbstraction/GLTexture.jl | 1 - GLMakie/src/GLAbstraction/GLTypes.jl | 6 +++--- GLMakie/src/screen.jl | 4 ---- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl index fe4019088c3..f5e3fbdac8b 100644 --- a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl +++ b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl @@ -88,10 +88,6 @@ mutable struct GPUVector{T} <: GPUArray{T, 1} buffer size real_length - - function GPUVector{T}(buffer, size, real_length) where T - new{T}(buffer, size, real_length) - end end GPUVector(x::GPUArray) = GPUVector{eltype(x)}(x, size(x), length(x)) diff --git a/GLMakie/src/GLAbstraction/GLTexture.jl b/GLMakie/src/GLAbstraction/GLTexture.jl index 11d8b5a6a28..a03317eb985 100644 --- a/GLMakie/src/GLAbstraction/GLTexture.jl +++ b/GLMakie/src/GLAbstraction/GLTexture.jl @@ -50,7 +50,6 @@ mutable struct TextureBuffer{T <: GLArrayEltypes} <: OpenglTexture{T, 1} requires_update::Observable{Bool} function TextureBuffer(texture::Texture{T, 1}, buffer::GLBuffer{T}) where T - # do we need to listen to texture changes or is buffer enough? x = map((_, _) -> true, buffer.requires_update, texture.requires_update) new{T}(texture, buffer, x) end diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index d28cac15ec0..4febcbc01cf 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -318,9 +318,9 @@ mutable struct RenderObject{Pre} prerenderfunctions, postrenderfunctions, id, true ) - # Checking each robj.requires_update after polling events seems like - # a better idea than having an observable chain... - for (key, uniform) in uniforms + + # gather update requests for polling in renderloop + for uniform in values(uniforms) if uniform isa Observable on(uniform) do _ robj.requires_update = true diff --git a/GLMakie/src/screen.jl b/GLMakie/src/screen.jl index a080b5be0dd..67072188ec1 100644 --- a/GLMakie/src/screen.jl +++ b/GLMakie/src/screen.jl @@ -678,17 +678,13 @@ function requires_update(screen::Screen) return false end -const UPDATES = Ref(0) - function on_demand_renderloop(screen::Screen) - UPDATES[] = 0 while isopen(screen) && !screen.stop_renderloop t = time_ns() time_per_frame = 1.0 / screen.config.framerate pollevents(screen) # GLFW poll if !screen.config.pause_renderloop && requires_update(screen) - UPDATES[] += 1 render_frame(screen) GLFW.SwapBuffers(to_native(screen)) end From ce00493251f01a3dc19d64afa621bfd3a1c41e5e Mon Sep 17 00:00:00 2001 From: ffreyer Date: Sat, 15 Oct 2022 15:46:49 +0200 Subject: [PATCH 07/11] remove :inspectable from uniforms --- GLMakie/src/drawing_primitives.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GLMakie/src/drawing_primitives.jl b/GLMakie/src/drawing_primitives.jl index 9d05119f4e3..8a6d225cd03 100644 --- a/GLMakie/src/drawing_primitives.jl +++ b/GLMakie/src/drawing_primitives.jl @@ -85,7 +85,7 @@ function cached_robj!(robj_func, screen, scene, x::AbstractPlot) !in(k, ( :transformation, :tickranges, :ticklabels, :raw, :SSAO, :lightposition, :material, - :inspector_label, :inspector_hover, :inspector_clear + :inspector_label, :inspector_hover, :inspector_clear, :inspectable )) end From 781e718971ac9c78cce549fedc1c36517fd66d45 Mon Sep 17 00:00:00 2001 From: ffreyer Date: Sat, 15 Oct 2022 15:51:30 +0200 Subject: [PATCH 08/11] update NEWS --- NEWS.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 7833c046be7..4c42cd4a615 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,7 +2,11 @@ ## master -- `hexbin` is now available as a recipe [#368] (https://github.com/JuliaPlots/Makie.jl/pull/2201) +- Add "render_on_demand" flag for GLMakie. Setting this to `true` will skip rendering until plots get updated. [#2336](https://github.com/MakieOrg/Makie.jl/pull/2336) + +## v0.18.0 + +- `hexbin` is now available as a recipe [#2201](https://github.com/JuliaPlots/Makie.jl/pull/2201) - Added the `tricontourf` plotting function [#2226](https://github.com/JuliaPlots/Makie.jl/pull/2226). - Fix per character attributes in text [#2244](https://github.com/JuliaPlots/Makie.jl/pull/2244) - `Axis` does now accept both a `Bool` and a `Tuple{Bool, Bool}` as values for `xtrimspine` and `ytrimspine` to trim only one end of the spine [#2171](https://github.com/JuliaPlots/Makie.jl/pull/2171). From 61a316385430b35dd39afff61df8c5c73fa8969a Mon Sep 17 00:00:00 2001 From: ffreyer Date: Sun, 16 Oct 2022 19:56:29 +0200 Subject: [PATCH 09/11] only track updates when render_on_demand = true --- GLMakie/src/GLAbstraction/AbstractGPUArray.jl | 9 ++-- GLMakie/src/GLAbstraction/GLTypes.jl | 46 +++++++++++++++---- GLMakie/src/drawing_primitives.jl | 1 + 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl index f5e3fbdac8b..d2be8d3016a 100644 --- a/GLMakie/src/GLAbstraction/AbstractGPUArray.jl +++ b/GLMakie/src/GLAbstraction/AbstractGPUArray.jl @@ -193,11 +193,10 @@ max_dim(t) = error("max_dim not implemented for: $(typeof(t)). This happen function (::Type{T})(x::Observable; kw...) where T <: GPUArray gpu_mem = T(x[]; kw...) - on(x) do x - gpu_mem.requires_update[] = true - update!(gpu_mem, x) - end - gpu_mem + # TODO merge these and handle update tracking during contruction + map!(_ -> true, gpu_mem.requires_update, x) + on(x -> update!(gpu_mem, x), x) + return gpu_mem end const BaseSerializer = Serialization.AbstractSerializer diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index 4febcbc01cf..108db49044e 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -298,11 +298,13 @@ mutable struct RenderObject{Pre} postrenderfunction id::UInt32 requires_update::Bool + function RenderObject{Pre}( context, uniforms::Dict{Symbol,Any}, observables::Vector{Observable}, vertexarray::GLVertexArray, - prerenderfunctions, postrenderfunctions + prerenderfunctions, postrenderfunctions, + track_updates = true ) where Pre fxaa = to_value(pop!(uniforms, :fxaa, true)) RENDER_OBJECT_ID_COUNTER[] += one(UInt32) @@ -319,19 +321,37 @@ mutable struct RenderObject{Pre} id, true ) - # gather update requests for polling in renderloop - for uniform in values(uniforms) - if uniform isa Observable - on(uniform) do _ - robj.requires_update = true + if track_updates + # gather update requests for polling in renderloop + for uniform in values(uniforms) + if uniform isa Observable + on(uniform) do _ + robj.requires_update = true + end + elseif uniform isa GPUArray + on(uniform.requires_update) do _ + robj.requires_update = true + end end - elseif uniform isa Union{Texture, TextureBuffer, GLBuffer} - on(uniform.requires_update) do _ - robj.requires_update = true + end + on(_ -> robj.requires_update = true, vertexarray.requires_update) + else + # remove tracking from GPUArrays + for uniform in values(uniforms) + if uniform isa GPUArray + foreach(off, uniform.requires_update.inputs) + empty!(uniform.requires_update.inputs) + end + end + for buffer in vertexarray.buffers + if buffer isa GPUArray + foreach(off, buffer.requires_update.inputs) + empty!(buffer.requires_update.inputs) end end + foreach(off, vertexarray.requires_update.inputs) + empty!(vertexarray.requires_update.inputs) end - on(_ -> robj.requires_update = true, vertexarray.requires_update) return robj end @@ -345,6 +365,11 @@ function RenderObject( switch_context!(context) + # This is a lazy workaround for disabling updates of `requires_update` when + # not rendering on demand. A cleaner implementation should probably go + # through @gen_defaults! and adjust constructors instead. + track_updates = to_value(pop!(data, :track_updates, true)) + targets = get(data, :gl_convert_targets, Dict()) delete!(data, :gl_convert_targets) passthrough = Dict{Symbol,Any}() # we also save a few non opengl related values in data @@ -387,6 +412,7 @@ function RenderObject( vertexarray, pre, post, + track_updates ) # automatically integrate object ID, will be discarded if shader doesn't use it robj[:objectid] = robj.id diff --git a/GLMakie/src/drawing_primitives.jl b/GLMakie/src/drawing_primitives.jl index 8a6d225cd03..a7fe1eed357 100644 --- a/GLMakie/src/drawing_primitives.jl +++ b/GLMakie/src/drawing_primitives.jl @@ -105,6 +105,7 @@ function cached_robj!(robj_func, screen, scene, x::AbstractPlot) if !isnothing(ambientlight) gl_attributes[:ambient] = ambientlight.color end + gl_attributes[:track_updates] = screen.config.render_on_demand robj = robj_func(gl_attributes) From 8d0bfd66d549e2f10ff653860019749510ce3532 Mon Sep 17 00:00:00 2001 From: ffreyer Date: Sun, 23 Oct 2022 19:48:10 +0200 Subject: [PATCH 10/11] move visible to struct & fix rerender on visible --- GLMakie/src/GLAbstraction/GLRender.jl | 4 +-- GLMakie/src/GLAbstraction/GLTypes.jl | 37 ++++++++++++++++++++------- GLMakie/src/screen.jl | 5 +--- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/GLMakie/src/GLAbstraction/GLRender.jl b/GLMakie/src/GLAbstraction/GLRender.jl index a0ff7119865..7513ad73066 100644 --- a/GLMakie/src/GLAbstraction/GLRender.jl +++ b/GLMakie/src/GLAbstraction/GLRender.jl @@ -15,7 +15,7 @@ function render(list::Vector{RenderObject{Pre}}) where Pre glUseProgram(program.id) glBindVertexArray(vertexarray.id) for renderobject in list - Bool(to_value(renderobject.uniforms[:visible])) || continue # skip invisible + renderobject.visible || continue # skip invisible # make sure we only bind new programs and vertexarray when it is actually # different from the previous one if renderobject.vertexarray != vertexarray @@ -55,7 +55,7 @@ So rewriting this function could get us a lot of performance for scenes with a lot of objects. """ function render(renderobject::RenderObject, vertexarray=renderobject.vertexarray) - if Bool(to_value(renderobject.uniforms[:visible])) + if renderobject.visible renderobject.requires_update = false renderobject.prerenderfunction() diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index 108db49044e..5b59739fd74 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -298,6 +298,7 @@ mutable struct RenderObject{Pre} postrenderfunction id::UInt32 requires_update::Bool + visible::Bool function RenderObject{Pre}( context, @@ -318,24 +319,42 @@ mutable struct RenderObject{Pre} context, uniforms, observables, vertexarray, prerenderfunctions, postrenderfunctions, - id, true + id, true, true ) + visible = pop!(uniforms, :visible, Observable(true)) + if track_updates + # visible changes should always trigger updates so that plots + # actually become invisible when visible is changed. + # Other uniforms and buffers don't need to trigger updates when + # visible = false + on(visible) do visible + robj.requires_update = true + robj.visible = visible + end + + function request_update(_::Any) + if robj.visible + robj.requires_update = true + end + return + end + # gather update requests for polling in renderloop for uniform in values(uniforms) if uniform isa Observable - on(uniform) do _ - robj.requires_update = true - end + on(request_update, uniform) elseif uniform isa GPUArray - on(uniform.requires_update) do _ - robj.requires_update = true - end + on(request_update, uniform.requires_update) end end - on(_ -> robj.requires_update = true, vertexarray.requires_update) + on(request_update, vertexarray.requires_update) else + on(visible) do visible + robj.visible = visible + end + # remove tracking from GPUArrays for uniform in values(uniforms) if uniform isa GPUArray @@ -401,7 +420,7 @@ function RenderObject( end buffers = filter(((key, value),) -> isa(value, GLBuffer) || key == :indices, data) uniforms = filter(((key, value),) -> !isa(value, GLBuffer) && key != :indices, data) - get!(data, :visible, true) # make sure, visibility is set + get!(data, :visible, Observable(true)) # make sure this exists merge!(data, passthrough) # in the end, we insert back the non opengl data, to keep things simple p = gl_convert(to_value(program), data) # "compile" lazyshader vertexarray = GLVertexArray(Dict(buffers), p) diff --git a/GLMakie/src/screen.jl b/GLMakie/src/screen.jl index 67072188ec1..e6d46b7808d 100644 --- a/GLMakie/src/screen.jl +++ b/GLMakie/src/screen.jl @@ -670,10 +670,7 @@ end function requires_update(screen::Screen) for (_, _, robj) in screen.renderlist - visible = Bool(to_value(get(robj.uniforms, :visible, true))) - if visible && robj.requires_update - return true - end + robj.requires_update && return true end return false end From 63dc419c0882f199dabcc6df15a564894fb675d5 Mon Sep 17 00:00:00 2001 From: ffreyer Date: Tue, 25 Oct 2022 10:14:29 +0200 Subject: [PATCH 11/11] fix visible initialization --- GLMakie/src/GLAbstraction/GLTypes.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index 5b59739fd74..21fc9e40e26 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -315,23 +315,23 @@ mutable struct RenderObject{Pre} # But with this implementation, the fxaa flag can't be changed, # and since this is a UUID, it shouldn't matter id = pack_bool(RENDER_OBJECT_ID_COUNTER[], fxaa) + visible = pop!(uniforms, :visible, Observable(true)) + robj = new( context, uniforms, observables, vertexarray, prerenderfunctions, postrenderfunctions, - id, true, true + id, true, visible[] ) - visible = pop!(uniforms, :visible, Observable(true)) - if track_updates # visible changes should always trigger updates so that plots # actually become invisible when visible is changed. # Other uniforms and buffers don't need to trigger updates when # visible = false on(visible) do visible - robj.requires_update = true robj.visible = visible + robj.requires_update = true end function request_update(_::Any)