Skip to content

Commit 1e78f6e

Browse files
committed
[GL] Allocate real textures for NullTexture
This commit changes how NullTexture is handled by the GL backend. Prior to this commit, NullTexture was simply translated to the default texture with handle 0. This resulted to a lot of debug output warnings when sampled from a shader (observed on NVIDIA). Using a real (uninitialized) texture avoids these warnings.
1 parent 03a74fb commit 1e78f6e

5 files changed

Lines changed: 138 additions & 33 deletions

File tree

RELEASE_NOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
- [GL] Added `Context.GetDebugMessages`
22
- [GL] Added `Context.OnDispose`
3+
- [GL] Represent `NullTexture` with a proper texture object instead of a texture with handle 0.
34

45
### 5.6.5
56
- Fixed support for 64-bit attributes and uniforms

src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,12 @@ type Texture =
8989
GL.DeleteTexture x.Handle
9090
ResourceCounts.removeTexture x.Context x.SizeInBytes
9191
GL.Check "could not delete texture"
92+
x.Handle <- 0
9293

9394
member x.Dispose() =
94-
if notNull x.Context then // NullTexture has no context
95-
using x.Context.ResourceLock (fun _ ->
96-
x.Destroy()
97-
x.Handle <- 0
98-
)
95+
using x.Context.ResourceLock (fun _ ->
96+
x.Destroy()
97+
)
9998

10099
interface IBackendTexture with
101100
member x.Runtime = x.Context.Runtime :> ITextureRuntime
@@ -148,6 +147,15 @@ type Texture =
148147
new Texture(ctx, handle, dimension, mipMapLevels, multisamples, size, cnt, isArray, format)
149148
end
150149

150+
type internal NullTexture(inner : Texture) =
151+
inherit Texture(inner.Context, inner.Handle, inner.Dimension, inner.MipMapLevels, inner.Multisamples,
152+
V3i.Zero, inner.Count, inner.IsArray, inner.Format, inner.SizeInBytes)
153+
override this.Destroy() = ()
154+
155+
override _.Name
156+
with get() = base.Name
157+
and set _ = ()
158+
151159
type internal SharedTexture(ctx : Context, handle : int, external : IExportedBackendTexture, memory : SharedMemoryBlock) =
152160
inherit Texture(ctx, handle,
153161
external.Dimension, external.MipMapLevels, external.Samples, external.Size,
@@ -168,10 +176,12 @@ type TextureViewHandle(ctx : Context, handle : int, dimension : TextureDimension
168176
GL.DeleteTexture x.Handle
169177
ResourceCounts.removeTextureView x.Context
170178
GL.Check "could not delete texture view"
179+
x.Handle <- 0
171180

172181
// Used for determining the bind target of null textures
173182
type internal TextureProperties =
174183
{ Dimension : TextureDimension
184+
Format : TextureFormat
175185
IsMultisampled : bool
176186
IsArray : bool }
177187

@@ -184,13 +194,25 @@ module internal TextureUtilitiesAndExtensions =
184194

185195
type FShade.GLSL.GLSLSamplerType with
186196
member x.Properties =
197+
let format =
198+
if x.isShadow then TextureFormat.DepthComponent32f
199+
elif x.IsInteger then TextureFormat.Rgba8i
200+
else TextureFormat.Rgba8
201+
187202
{ Dimension = x.dimension.TextureDimension
203+
Format = format
188204
IsMultisampled = x.isMS
189205
IsArray = x.isArray }
190206

191207
type FShade.GLSL.GLSLImageType with
192208
member x.Properties =
209+
let format =
210+
match x.format with
211+
| Some fmt -> fmt.TextureFormat
212+
| _ -> if x.IsInteger then TextureFormat.Rgba8i else TextureFormat.Rgba8
213+
193214
{ Dimension = x.dimension.TextureDimension
215+
Format = format
194216
IsMultisampled = x.isMS
195217
IsArray = x.isArray }
196218

@@ -658,14 +680,36 @@ module TextureCreationExtensions =
658680
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
659681
module Texture =
660682

661-
let private emptyStore = ConcurrentDictionary<TextureProperties, Texture>()
683+
let internal getNull =
684+
let store = ConcurrentDictionary<Context * TextureProperties, Lazy<NullTexture>>()
685+
686+
fun (properties : TextureProperties) (context : Context) ->
687+
let texture =
688+
store.GetOrAdd((context, properties), fun (context, properties as key) ->
689+
lazy (
690+
let inner =
691+
let count = if properties.IsArray then 1 else 0
692+
let samples = if properties.IsMultisampled then 2 else 1
693+
694+
context.CreateTexture(
695+
V3i 8, properties.Dimension, properties.Format,
696+
slices = count, levels = 1, samples = samples
697+
)
698+
699+
inner.Name <- "Null"
700+
let texture = new NullTexture(inner)
701+
702+
context.OnDispose.Add(fun _ ->
703+
store.TryRemove key |> ignore
704+
inner.Dispose()
705+
texture.Handle <- 0
706+
)
707+
708+
texture
709+
)
710+
).Value
662711

663-
let internal empty (properties : TextureProperties) =
664-
emptyStore.GetOrAdd(properties, fun _ ->
665-
let count = if properties.IsArray then Some 1 else None
666-
let samples = if properties.IsMultisampled then 2 else 1
667-
new Texture(null, 0, properties.Dimension, 1, samples, V3i.Zero, count, TextureFormat.Rgba8, 0L)
668-
)
712+
texture :> Texture
669713

670714
let create1D (c : Context) (size : int) (mipLevels : int) (format : TextureFormat) =
671715
c.CreateTexture1D(size, mipLevels, format)

src/Aardvark.Rendering.GL/Resources/Textures/TextureUpload.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,13 @@ module ContextTextureUploadExtensions =
366366
Texture.uploadPixVolume texture info.HasWantMipMaps 0 V3i.Zero data
367367
texture
368368

369-
| :? NullTexture ->
369+
| :? Aardvark.Rendering.NullTexture ->
370370
let properties =
371371
match properties with
372372
| ValueSome p -> p
373373
| _ -> failf "cannot prepare null texture without properties"
374374

375-
Texture.empty properties
375+
Texture.getNull properties this
376376

377377
| :? Texture as o ->
378378
o

src/Aardvark.Rendering/Resources/Textures/TextureExtensions.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ type ITextureRuntimeExtensions private() =
300300
[<Optional; DefaultParameterValue(0)>] slice : int,
301301
[<Optional; DefaultParameterValue(V2i())>] offset : V2i,
302302
[<Optional; DefaultParameterValue(V2i())>] size : V2i) =
303-
this.Upload(texture.[TextureAspect.Color, level, slice], source, offset, size)
303+
this.Upload(texture.[texture.Format.Aspect, level, slice], source, offset, size)
304304

305305
// ================================================================================================================
306306
// Upload 3D
@@ -344,7 +344,7 @@ type ITextureRuntimeExtensions private() =
344344
[<Optional; DefaultParameterValue(0)>] slice : int,
345345
[<Optional; DefaultParameterValue(V3i())>] offset : V3i,
346346
[<Optional; DefaultParameterValue(V3i())>] size : V3i) =
347-
this.Upload(texture.[TextureAspect.Color, level, slice], source, offset, size)
347+
this.Upload(texture.[texture.Format.Aspect, level, slice], source, offset, size)
348348

349349

350350
// ================================================================================================================

src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Upload.fs

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,11 @@ module TextureUpload =
145145
let randomBuffer = randomTask |> RenderTask.renderToColor (AVal.init <| V2i(256))
146146
randomBuffer.Acquire()
147147

148-
try randomBuffer.GetValue() |> ignore
149-
finally randomBuffer.Release()
148+
let random =
149+
try
150+
randomBuffer.GetValue().Download().AsPixImage<uint8>()
151+
finally
152+
randomBuffer.Release()
150153

151154
use task =
152155
Sg.fullScreenQuad
@@ -155,11 +158,29 @@ module TextureUpload =
155158
|> Sg.compile runtime signature
156159

157160
// Render sampling the null texture.
158-
// We can't check the result since null textures are uninitialized, just make sure there are no errors.
161+
// We can't check the result since null textures are uninitialized, just make sure there are no errors, and we
162+
// get a different result than with the random texture.
159163
let buffer = task |> RenderTask.renderToColor (AVal.init <| V2i(256))
160164
buffer.Acquire()
161-
try buffer.GetValue() |> ignore
162-
finally buffer.Release()
165+
166+
let result =
167+
try
168+
buffer.GetValue().Download().AsPixImage<uint8>()
169+
finally
170+
buffer.Release()
171+
172+
Expect.equal result.Size random.Size "Unexpected result size"
173+
Expect.notEqual result.Data random.Data "Output with null texture matches output with random texture"
174+
175+
// Make sure we bound a real texture by checking the GL debug output
176+
match runtime with
177+
| :? GL.Runtime as runtime ->
178+
let messages = runtime.Context.GetDebugMessages()
179+
for msg in messages do
180+
if msg.Message.Contains "The texture object (0) bound" then
181+
failtest msg.Message
182+
| _ ->
183+
()
163184

164185
let texture1DNull (runtime : IRuntime) =
165186
let diffuseSampler =
@@ -725,6 +746,21 @@ module TextureUpload =
725746

726747
runtime |> renderQuadWithNullTexture shader randomTexture
727748

749+
let private renderRandomToTexture2D (texture: IBackendTexture) (runtime: IRuntime) =
750+
use signature = runtime.CreateFramebufferSignature([DefaultSemantic.Colors, TextureFormat.Rgba8], samples = texture.Samples)
751+
use framebuffer = runtime.CreateFramebuffer(signature, [DefaultSemantic.Colors, texture.GetOutputView()])
752+
753+
use ss = runtime.CreateTexture2D(texture.Size.XY, TextureFormat.Rgba8)
754+
ss.Upload(PixImage.random8ui texture.Size.XY)
755+
756+
use task =
757+
Sg.fullScreenQuad
758+
|> Sg.diffuseTexture' ss
759+
|> Sg.shader { do! DefaultSurfaces.diffuseTexture }
760+
|> Sg.compile runtime signature
761+
762+
task.Run(framebuffer)
763+
728764
let texture2DNullMultisampled (runtime : IRuntime) =
729765
let diffuseSampler =
730766
sampler2dMS {
@@ -736,15 +772,21 @@ module TextureUpload =
736772

737773
let diffuseTexture (v : Effects.Vertex) =
738774
fragment {
739-
return diffuseSampler.Read(V2i.Zero, 0)
775+
let p = V2i (v.tc * V2f diffuseSampler.Size.XY)
776+
return diffuseSampler.Read(p, 0)
740777
}
741778

742779
let shader =
743780
Sg.shader {
744781
do! diffuseTexture
745782
}
746783

747-
runtime |> renderQuadWithNullTexture shader nullTexture
784+
use randomTexture =
785+
let texture = runtime.CreateTexture2D(V2i(8, 8), TextureFormat.Rgba8, samples = 2)
786+
renderRandomToTexture2D texture runtime
787+
texture
788+
789+
runtime |> renderQuadWithNullTexture shader randomTexture
748790

749791
let texture2DNullMultisampledArray (runtime : IRuntime) =
750792
let diffuseSampler =
@@ -757,15 +799,21 @@ module TextureUpload =
757799

758800
let diffuseTexture (v : Effects.Vertex) =
759801
fragment {
760-
return diffuseSampler.Read(V2i.Zero, 0, 0)
802+
let p = V2i (v.tc * V2f diffuseSampler.Size.XY)
803+
return diffuseSampler.Read(p, 0, 0)
761804
}
762805

763806
let shader =
764807
Sg.shader {
765808
do! diffuseTexture
766809
}
767810

768-
runtime |> renderQuadWithNullTexture shader nullTexture
811+
use randomTexture =
812+
let texture = runtime.CreateTexture2DArray(V2i(8, 8), TextureFormat.Rgba8, samples = 2, count = 1)
813+
renderRandomToTexture2D texture runtime
814+
texture
815+
816+
runtime |> renderQuadWithNullTexture shader randomTexture
769817

770818
let texture2DNullInt(runtime : IRuntime) =
771819
let diffuseSampler =
@@ -778,7 +826,8 @@ module TextureUpload =
778826

779827
let diffuseTexture (v : Effects.Vertex) =
780828
fragment {
781-
return V4f(diffuseSampler.Read(V2i.Zero, 0))
829+
let p = V2i (v.tc * V2f diffuseSampler.Size.XY)
830+
return V4f(diffuseSampler.Read(p, 0))
782831
}
783832

784833
let shader =
@@ -798,19 +847,23 @@ module TextureUpload =
798847
filter Filter.MinMagLinear
799848
addressU WrapMode.Clamp
800849
addressV WrapMode.Clamp
850+
comparison ComparisonFunction.LessOrEqual
801851
}
802852

803853
let diffuseTexture (v : Effects.Vertex) =
804854
fragment {
805-
return V4f(diffuseSampler.Sample(v.tc, 0.0f))
855+
return V4f(diffuseSampler.Sample(v.tc, 0.5f))
806856
}
807857

808858
let shader =
809859
Sg.shader {
810860
do! diffuseTexture
811861
}
812862

813-
runtime |> renderQuadWithNullTexture shader nullTexture
863+
use randomTexture = runtime.CreateTexture2D(V2i(8, 8), TextureFormat.DepthComponent32f)
864+
randomTexture.Upload(PixImage.random32f' Col.Format.Gray <| V2i(8, 8))
865+
866+
runtime |> renderQuadWithNullTexture shader randomTexture
814867

815868
let texture2DStreaming (runtime : IRuntime) =
816869
let texture = runtime.CreateStreamingTexture(false)
@@ -997,7 +1050,6 @@ module TextureUpload =
9971050
else
9981051
runtime.CreateTextureCube(size, fmt, levels)
9991052

1000-
10011053
try
10021054
let target = texture.[TextureAspect.Color, level, slice]
10031055
target.Upload(data, regionOffset, regionSize)
@@ -1114,15 +1166,19 @@ module TextureUpload =
11141166

11151167
let diffuseTexture (v : Effects.Vertex) =
11161168
fragment {
1117-
return diffuseSampler.Sample(V3f.Zero)
1169+
let tc = v.tc * 2.0f - 1.0f
1170+
return diffuseSampler.Sample(V3f(tc.X, -tc.Y, 1.0f)) // +Z face (slice = 4)
11181171
}
11191172

11201173
let shader =
11211174
Sg.shader {
11221175
do! diffuseTexture
11231176
}
11241177

1125-
runtime |> renderQuadWithNullTexture shader nullTexture
1178+
use randomTexture = runtime.CreateTextureCube(8, TextureFormat.Rgba8)
1179+
randomTexture.Upload(PixImage.random8ui <| V2i(8, 8), slice = 4)
1180+
1181+
runtime |> renderQuadWithNullTexture shader randomTexture
11261182

11271183
let textureCubeNullArray (runtime : IRuntime) =
11281184
let diffuseSampler =
@@ -1135,15 +1191,19 @@ module TextureUpload =
11351191

11361192
let diffuseTexture (v : Effects.Vertex) =
11371193
fragment {
1138-
return diffuseSampler.Sample(V3f.Zero, 32)
1194+
let tc = v.tc * 2.0f - 1.0f
1195+
return diffuseSampler.Sample(V3f(tc.X, -tc.Y, 1.0f), 0) // +Z face (slice = 4)
11391196
}
11401197

11411198
let shader =
11421199
Sg.shader {
11431200
do! diffuseTexture
11441201
}
11451202

1146-
runtime |> renderQuadWithNullTexture shader nullTexture
1203+
use randomTexture = runtime.CreateTextureCubeArray(8, TextureFormat.Rgba8, count = 1)
1204+
randomTexture.Upload(PixImage.random8ui <| V2i(8, 8), slice = 4)
1205+
1206+
runtime |> renderQuadWithNullTexture shader randomTexture
11471207

11481208
let tests (backend : Backend) =
11491209
[

0 commit comments

Comments
 (0)