Skip to content

SDL 3.4.0 functions, structs and defines#31

Open
Abev08 wants to merge 2 commits intoJupiterRider:commentsfrom
Abev08:comments
Open

SDL 3.4.0 functions, structs and defines#31
Abev08 wants to merge 2 commits intoJupiterRider:commentsfrom
Abev08:comments

Conversation

@Abev08
Copy link
Contributor

@Abev08 Abev08 commented Jan 10, 2026

Hi. I have added functions, structs and defines that are new in SDL 3.4.0 release.

While doing it I noticed some missing functions and structs that were present in 3.2.0 release - also added these.

From the new functions I have tested and confirmed that they work:

  • GetPixelFormatFromGPUTextureFormat
  • GetGPUTextureFormatFromPixelFormat
  • GetGPUDeviceProperties
  • CreateGPURenderer
  • GetGPURendererDevice
  • SetDefaultTextureScaleMode
  • GetDefaultTextureScaleMode
  • SetRenderTextureAddressMode
  • GetRenderTextureAddressMode
  • RenderTexture9GridTiled
  • StretchSurface
  • LoadSurface
  • LoadPNG
  • SavePNG
  • SetWindowProgressState
  • GetWindowProgressState
  • SetWindowProgressValue
  • GetWindowProgressValue
  • and everything in sdl_cpuinfo.go file

I don't know how to test out two new functions, but I called them, and they returned some SDL_Error and didn't crash or cause the app to malfunction, so I guess they work?

  • SetTexturePalette
  • GetTexturePalette

SDL 3.4.0 adds more functions than these mentioned above. I have created bindings for them and commented them out as they are not tested.

The test app I used to test out new functions:

package main

import (
	"fmt"

	"github.com/jupiterrider/purego-sdl3/sdl"
)

func main() {
	defer sdl.Quit()
	if !sdl.Init(sdl.InitVideo) {
		panic(sdl.GetError())
	}

	{
		maj, min, patch := sdl.GetVersion()
		fmt.Printf("SDL3 version: %d.%d.%d\n", maj, min, patch)
		printCPUFeatures()
	}

	window := sdl.CreateWindow("test", 1280, 720, sdl.WindowResizable)
	if window == nil {
		panic(sdl.GetError())
	}
	defer sdl.DestroyWindow(window)

	renderer := sdl.CreateGPURenderer(nil, window)
	if renderer == nil {
		panic(sdl.GetError())
	}
	defer sdl.DestroyRenderer(renderer)

	if !sdl.SetRenderVSync(renderer, 1) {
		panic(sdl.GetError())
	}

	device := sdl.GetGPURendererDevice(renderer)
	if device == nil {
		fmt.Printf("GPURenderer device acquisition failed err: %s\n", sdl.GetError())
	} else {
		fmt.Printf("GPURenderer device driver: %s\n", sdl.GetGPUDeviceDriver(device))
		fmt.Printf("GPURenderer device properties: %v\n", sdl.GetGPUDeviceProperties(device))
	}

	sur1 := sdl.LoadSurface("./examples/renderer/20-sprite-batching/gopher-happy.png")
	sur2 := sdl.LoadPNG("./examples/renderer/20-sprite-batching/gopher-happy.png")
	sur3 := sdl.CreateSurface(sur1.W*2, sur1.H, sur1.Format)
	if !sdl.StretchSurface(sur1, nil, sur3, nil, sdl.ScaleModePixelArt) {
		panic(sdl.GetError())
	}
	if sur1 == nil || sur2 == nil || sur3 == nil {
		panic(sdl.GetError())
	}
	defer func() {
		sdl.DestroySurface(sur1)
		sdl.DestroySurface(sur2)
		sdl.DestroySurface(sur3)
	}()

	if !sdl.SavePNG(sur3, "out.png") {
		panic(sdl.GetError())
	}

	tex1 := sdl.CreateTextureFromSurface(renderer, sur1)
	sdl.SetDefaultTextureScaleMode(renderer, sdl.ScaleModePixelArt)
	sdl.SetRenderTextureAddressMode(renderer, sdl.TextureAddressWrap, sdl.TextureAddressWrap)
	tex2 := sdl.CreateTextureFromSurface(renderer, sur2)
	tex3 := sdl.CreateTextureFromSurface(renderer, sur3)
	if tex1 == nil || tex2 == nil || tex3 == nil {
		panic(sdl.GetError())
	}
	defer func() {
		sdl.DestroyTexture(tex1)
		sdl.DestroyTexture(tex2)
		sdl.DestroyTexture(tex3)
	}()

	var scaleMode sdl.ScaleMode
	sdl.GetDefaultTextureScaleMode(renderer, &scaleMode)
	fmt.Printf("GPURenderer default scale mode: %d\n", scaleMode)
	var uMode, vMode sdl.TextureAddressMode
	sdl.GetRenderTextureAddressMode(renderer, &uMode, &vMode)
	fmt.Printf("GPURenderer texture address mode u: %d, v: %d\n", uMode, vMode)

	textureFormat := sdl.GetGPUTextureFormatFromPixelFormat(tex3.Format)
	fmt.Printf("GPURenderer texture format: %v\n", textureFormat)
	pixelFormat := sdl.GetPixelFormatFromGPUTextureFormat(textureFormat)
	fmt.Printf("GPURenderer pixel format: %v\n", pixelFormat)

	white := sdl.FColor{R: 1.0, G: 1.0, B: 1.0, A: 1.0}

	sdl.SetWindowProgressState(window, sdl.ProgressStateNormal)
	progressState := sdl.GetWindowProgressState(window)
	fmt.Printf("Window progress state: %v\n", progressState)

	progressFinished := false
	running := true
	for running {
		var event sdl.Event
		for sdl.PollEvent(&event) {
			switch event.Type() {
			case sdl.EventQuit:
				running = false
			}
		}

		sdl.SetRenderDrawColor(renderer, 20, 20, 20, 255)
		sdl.RenderClear(renderer)

		sdl.SetRenderDrawColor(renderer, 255, 255, 255, 255)
		if !progressFinished {
			progressValue := sdl.GetWindowProgressValue(window)
			sdl.RenderDebugTextFormat(renderer, 5, 5, "Window progress value: %.3f", progressValue)
			sdl.SetWindowProgressValue(window, progressValue+0.001)
			progressFinished = progressValue >= 1
		} else {
			sdl.RenderDebugText(renderer, 5, 5, "Window progress finished")
		}

		sdl.SetRenderDrawColor(renderer, 255, 0, 0, 255)
		sdl.RenderFillRect(renderer, &sdl.FRect{X: 100, Y: 200, W: 200, H: 100})

		sdl.RenderTexture(renderer, tex1, nil, &sdl.FRect{X: 400, Y: 120, W: float32(sur1.W * 5), H: float32(sur1.H * 5)})
		sdl.RenderTexture(renderer, tex2, nil, &sdl.FRect{X: 600, Y: 120, W: float32(sur2.W * 5), H: float32(sur2.H * 5)})
		sdl.RenderTexture(renderer, tex2, nil, &sdl.FRect{X: 600, Y: 300, W: float32(sur3.W * 5), H: float32(sur3.H * 5)})

		sdl.RenderGeometry(renderer, tex2, []sdl.Vertex{
			{Position: sdl.FPoint{X: 100, Y: 400}, Color: white, TexCoord: sdl.FPoint{X: 0, Y: 0}}, // top left
			{Position: sdl.FPoint{X: 200, Y: 400}, Color: white, TexCoord: sdl.FPoint{X: 2, Y: 0}}, // top right
			{Position: sdl.FPoint{X: 100, Y: 500}, Color: white, TexCoord: sdl.FPoint{X: 0, Y: 2}}, // bottom left
			{Position: sdl.FPoint{X: 200, Y: 500}, Color: white, TexCoord: sdl.FPoint{X: 2, Y: 2}}, // bottom right
		}, []int32{
			1, 0, 2,
			1, 2, 3,
		})

		sdl.RenderTexture9GridTiled(renderer, tex2, nil, 9, 9, 9, 9, 1, &sdl.FRect{X: 250, Y: 400, W: 300, H: 300}, 1)

		sdl.RenderPresent(renderer)
	}
}

func printCPUFeatures() {
	fmt.Printf("CPU L1 cache line size: %v bytes\n", sdl.GetCPUCacheLineSize())
	fmt.Printf("CPU number of logical cores: %v\n", sdl.GetNumLogicalCPUCores())
	fmt.Printf("SIMD alignment: %v bytes\n", sdl.GetSIMDAlignment())
	fmt.Printf("CPU supports AltiVec: %v\n", sdl.HasAltiVec())
	fmt.Printf("CPU supports ARM SIMD: %v\n", sdl.HasARMSIMD())
	fmt.Printf("CPU supports AVX: %v\n", sdl.HasAVX())
	fmt.Printf("CPU supports AVX2: %v\n", sdl.HasAVX2())
	fmt.Printf("CPU supports AVX-512F: %v\n", sdl.HasAVX512F())
	fmt.Printf("CPU supports LASX: %v\n", sdl.HasLASX())
	fmt.Printf("CPU supports LSX: %v\n", sdl.HasLSX())
	fmt.Printf("CPU supports MMX: %v\n", sdl.HasMMX())
	fmt.Printf("CPU supports NEON: %v\n", sdl.HasNEON())
	fmt.Printf("CPU supports SSE: %v\n", sdl.HasSSE())
	fmt.Printf("CPU supports SSE2: %v\n", sdl.HasSSE2())
	fmt.Printf("CPU supports SSE3: %v\n", sdl.HasSSE3())
	fmt.Printf("CPU supports SSE4.1: %v\n", sdl.HasSSE41())
	fmt.Printf("CPU supports SSE4.2: %v\n", sdl.HasSSE42())
	fmt.Printf("System RAM: %v MiB\n", sdl.GetSystemRAM())
	fmt.Printf("System size of a page of memory: %v bytes\n", sdl.GetSystemPageSize())
}

Another thing that I have noticed is that when GPUDevice is created by CreateGPURenderer() function (via CreateGPURenderer(nil, window) call and retrieved with GetGPURendererDevice()) it shouldn't be destroyed with DestroyGPUDevice(). Doing so causes segmentation violation error. I don't know if it's correct behavior. SDL wiki doesn't mention DestroyGPUDevice() usage on GPUDevice created this way. So I guess just leaving it to the OS to cleanup after the application is closed is fine.

@Abev08
Copy link
Contributor Author

Abev08 commented Jan 10, 2026

I wanted the 2nd commit to be separate pull request as it's not connected to this one but it didn't work out :(

I would like to suggest trying to load the libraries from current working directory as a priority. Before looking for them in the OS. It could be useful when trying out different library versions or when shipping out the application without requiring the user to have the library loaded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant