Image 2 GPU Error

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
FischBird
Prole
Posts: 4
Joined: Wed Apr 26, 2023 1:42 pm
Location: Germany

Image 2 GPU Error

Post by FischBird »

I was trying to make an compute shader using the pixel shader, but the Image is not correctly sendet to the GPU.
Using LÖVE 11.4.

main.lua

Code: Select all

cmp = love.graphics.newShader("compute.cmp")
test = false
MAX = 10
SIZE = 16

function sendData(input,weight,bias,output) -- float[]
    -- input  = Image[y  ]
    -- weight = Image[x,y]
    -- bias   = Image[x  ]
    -- output = int  (x  )
    local screen = love.graphics.getCanvas() -- for end output
    local level  = output
    local weightMap   = love.graphics.newCanvas(#weight,#weight[1],{format="rgba32f"}) -- x,y
    local weightMaper = weightMap:newImageData()

    local biasMap     = love.graphics.newCanvas(#weight,1,{format="rgba32f"})    -- x
    local biasMaper   = biasMap:newImageData()

    local intakeMap   = love.graphics.newCanvas(#weight[1],1,{format="rgba32f"}) -- y
    local intakeMaper = intakeMap:newImageData()

    print("dimensions:(" .. #weight .. "|" .. #weight[1] .. ")")
    print("dimension#:(" .. #bias   .. "|" .. #input ..")")

    if type(output) == "number" then
        level = love.graphics.newCanvas(output,1,{format="rgba32f"})
    end
    local f = 0

    for i=0,#bias - 1,1 do
        f = bias [i + 1]
        print("bias:(" .. i .. "):" .. f)
        biasMaper:setPixel(i,0,f,f,f,1)
    end

    for i=0,#input - 1,1 do
        f = input[i + 1]
        print("input:(" .. i .. "):" .. f)
        intakeMaper:setPixel(i,0,f,f,f,1)
    end

    for i=0,#weight - 1,1 do
        for j=0,#weight[1] - 1,1 do
            f = weight[i + 1][j + 1]
            -- print("weights:(" .. i .. "|" .. j .. "):" .. f)
            weightMaper:setPixel( i,j, f,f,f,1)
        end
    end
    weightMap  :release() -- free space
    biasMap    :release()
    intakeMap  :release()
    weightMap  = love.graphics.newImage(weightMaper) -- make Image
    intakeMap  = love.graphics.newImage(intakeMaper) -- for display and
    biasMap    = love.graphics.newImage(biasMaper  ) -- data use

    -- better display
    weightMap  :setFilter("nearest","nearest")
    biasMap    :setFilter("nearest","nearest")
    intakeMap  :setFilter("nearest","nearest")

    -- sending data to Shader
    cmp:send("height" ,#weight  [1])
    cmp:send("weights",weightMap   )
    cmp:send("intake" ,intakeMap   )
    cmp:send("bias"   ,biasMap     )
    
    love.graphics.rectangle("fill",                        0,       0,               500, 500)
    love.graphics.draw(intakeMap  ,                        0,       0,            0,SIZE,SIZE)
    love.graphics.draw(biasMap    ,#weight * SIZE + SIZE * 2,SIZE * 2,math.pi * 0.5,SIZE,SIZE)
    love.graphics.draw(weightMap  ,                        0,SIZE * 2,            0,SIZE,SIZE)
    -- love.graphics.draw()
    
    weightMap  :release() -- free space
    biasMap    :release()
    intakeMap  :release()
    weightMaper:release()
    biasMaper  :release()
    intakeMaper:release()

    -- use output buffer use Shader and compute!
    love.graphics.setCanvas(level)
    love.graphics.setShader(cmp)
    love.graphics.rectangle("fill",0,0,#weight * 2,5)
    love.graphics.setCanvas()
    love.graphics.setShader()
    -- data collector
    local outp = {}
    local data = level:newImageData()
    local r,g,b,a
    -- display purpeses
    level:setFilter("nearest","nearest")
    love.graphics.setCanvas(screen)
    love.graphics.draw(level,#weight * SIZE + SIZE * 4,SIZE * 2,math.pi * 0.5,SIZE,SIZE) -- display output
    -- collector
    for i=0,#weight - 1,1 do
        r,g,b,a = data:getPixel(i,0)
        print("Data:(" .. i .. "):(" .. r .. "," .. g .. "," .. b .. "," .. a ..")")
        outp[i + 1] = r
    end
    data:release() -- free
    return outp
end
function smallLog(value)
    for k,v in pairs(value) do
        print(k .. "\t|" .. v)
    end
end

function love.load()
    -- setupDisplay-buffer
    screen = love.graphics.newCanvas(800,500)
    -- no Crash?
    cmp:send("bias"   ,love.graphics.newCanvas(1,1,{format="rgba32f"}))
    cmp:send("intake" ,love.graphics.newCanvas(1,1,{format="rgba32f"}))
    cmp:send("weights",love.graphics.newCanvas(1,1,{format="rgba32f"}))
    cmp:send("height" ,0)
end

function love.update(dt)
end

function love.draw()
    love.graphics.setCanvas(screen)
    if test then
        -- test
        smallLog(
            sendData({0,.4,.8,.0},{{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},{0,.2,.4,.8},4)
        )
        test = false
    end
    -- drawing things
    love.graphics.setCanvas()
    love.graphics.setColor(1,1,1)
    love.graphics.draw(screen,0,0,0,1,1)
end

function love.keypressed( key, scancode, isrepeat )
    if key == "space" then
        test = true
    end
end
function love.mousepressed( x, y, button, istouch, presses )
    -- get Pixel value
    local s = screen:newImageData()
    print(s:getPixel(x,y))
    s:release()
end

compute.cmp

Code: Select all

// uniform vec2 size;
// #define BUFFER_SIZE 100

uniform int height = 0;
uniform Image bias;
// x output
uniform Image intake;
// y input
uniform Image weights;
// x is output y is input
// x + M * y

/*layout(std430,binding = 1) buffer moin
{};/**/

float activation(float value){
    return 1 / (1 + exp(-value));
}
vec3 activation(vec3 value){
    return 1 / (1 + exp(-value));
}

vec4 effect( vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords ){
    // x is for the output array
    // y is for the input array
    int x = int(floor(screen_coords.x));
    int y = 0;
    // vec3 col   = bias[x].rgb;
    // for(;y < height;y++){
    //     col += Texel(intake,vec2(y,0)).rgb * Texel(weights,vec2(x,y)).rgb;
    // }
    // return vec4(activation(col),1);
    float col   = Texel(bias,vec2(x,0)).r;
    for(;y < height;y++){
        col += Texel(intake,vec2(y,0)).r * Texel(weights,vec2(x,y)).r;
    }
    return vec4(
        activation(col),
        Texel(intake,vec2(x,0)).r,
        Texel(bias,vec2(x,0)).r,
        1);
}
This should have diffrent values in the output array (Texture), but it's overriden, when the Textures are transfered to the GPU.
On the CPU side the textures are normal and in the Shader x is diffrent.

If there are any questions, let me know, and if I make "bad" Code let me know.
Andlac028
Party member
Posts: 174
Joined: Fri Dec 14, 2018 2:27 pm
Location: Slovakia

Re: Image 2 GPU Error

Post by Andlac028 »

If you are interested in compute shaders, I recommend you to look at development version of love here, where I saw some commits implementing compute shaders.
FischBird
Prole
Posts: 4
Joined: Wed Apr 26, 2023 1:42 pm
Location: Germany

Re: Image 2 GPU Error

Post by FischBird »

I know, that LÖVE 12 has compute shaders.
But my questioning was about, if there is a workaround to my problem in the current version of LÖVE.

The GPU is receiving "0 | 0.8 | 0.8 | 0.8" as an Image,
but the Image that was send has data of "0 | 0.2 | 0.4 | 0.8" (both times referring to biasImage or biasMap).

The Image is drawn correctly, but not send correctly to the GPU.
User avatar
pgimeno
Party member
Posts: 3609
Joined: Sun Oct 18, 2015 2:58 pm

Re: Image 2 GPU Error

Post by pgimeno »

You know texture coordinates are normalized, right?
FischBird
Prole
Posts: 4
Joined: Wed Apr 26, 2023 1:42 pm
Location: Germany

Re: Image 2 GPU Error

Post by FischBird »

In this case, the Textures are in pixels(int) accessed.

I changed the pixel shader

Code: Select all

uniform int height = 1;
uniform int width  = 1;
uniform Image bias;
// x output
uniform Image intake;
// y input
uniform Image weights;
// x is output y is input
// x + M * y


float activation(float value){
    return 1 / (1 + exp(-value));
}

vec4 effect( vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords ){
    int x = int(floor(screen_coords.x));
    int y = 0;
    
    float col   = Texel(bias,vec2(x / width,0)).r;
    for(;y < height;y++){
        col += Texel(intake,vec2(y / height,0)).r * Texel(weights,vec2(x / width,y / height)).r;
    }
    return vec4(
        activation(col),
        Texel(intake,vec2(x / width,0)).r,
        Texel(bias,vec2(x / width,0)).r,
        1);
}
and the main

Code: Select all

cmp = love.graphics.newShader("compute.cmp")
test = false
MAX = 10
SIZE = 16

function sendData(input,weight,bias,output) -- float[]
    -- input  = Image[y  ]
    -- weight = Image[x,y]
    -- bias   = Image[x  ]
    -- output = int  (x  )
    local screen = love.graphics.getCanvas() -- for end output
    local level  = output
    local weightMap   = love.graphics.newCanvas(#weight,#weight[1],{format="rgba32f"}) -- x,y
    local weightMaper = weightMap:newImageData()

    local biasMap     = love.graphics.newCanvas(#weight,1,{format="rgba32f"})    -- x
    local biasMaper   = biasMap:newImageData()

    local intakeMap   = love.graphics.newCanvas(#weight[1],1,{format="rgba32f"}) -- y
    local intakeMaper = intakeMap:newImageData()

    print("dimensions:(" .. #weight .. "|" .. #weight[1] .. ")")
    print("dimension#:(" .. #bias   .. "|" .. #input ..")")

    if type(output) == "number" then
        level = love.graphics.newCanvas(output,1,{format="rgba32f"})
    end
    local f = 0

    for i=0,#bias - 1,1 do
        f = bias [i + 1]
        print("bias:(" .. i .. "):" .. f)
        biasMaper:setPixel(i,0,f,f,f,1)
    end

    for i=0,#input - 1,1 do
        f = input[i + 1]
        print("input:(" .. i .. "):" .. f)
        intakeMaper:setPixel(i,0,f,f,f,1)
    end

    for i=0,#weight - 1,1 do
        for j=0,#weight[1] - 1,1 do
            f = weight[i + 1][j + 1]
            -- print("weights:(" .. i .. "|" .. j .. "):" .. f)
            weightMaper:setPixel( i,j, f,f,f,1)
        end
    end
    weightMap  :release() -- free space
    biasMap    :release()
    intakeMap  :release()
    weightMap  = love.graphics.newImage(weightMaper) -- make Image
    intakeMap  = love.graphics.newImage(intakeMaper) -- for display and
    biasMap    = love.graphics.newImage(biasMaper  ) -- data use

    -- better display
    weightMap  :setFilter("nearest","nearest")
    biasMap    :setFilter("nearest","nearest")
    intakeMap  :setFilter("nearest","nearest")

    -- sending data to Shader
    cmp:send("height" ,#weight  [1])
    cmp:send("width"  ,#weight     ) -- change with normalisation
    cmp:send("weights",weightMap   )
    cmp:send("intake" ,intakeMap   )
    cmp:send("bias"   ,biasMap     )
    
    love.graphics.rectangle("fill",                        0,       0,               500, 500)
    love.graphics.draw(intakeMap  ,                        0,       0,            0,SIZE,SIZE)
    love.graphics.draw(biasMap    ,#weight * SIZE + SIZE * 2,SIZE * 2,math.pi * 0.5,SIZE,SIZE)
    love.graphics.draw(weightMap  ,                        0,SIZE * 2,            0,SIZE,SIZE)
    -- love.graphics.draw()
    
    weightMap  :release() -- free space
    biasMap    :release()
    intakeMap  :release()
    weightMaper:release()
    biasMaper  :release()
    intakeMaper:release()

    -- use output buffer use Shader and compute!
    love.graphics.setCanvas(level)
    love.graphics.setShader(cmp)
    love.graphics.rectangle("fill",0,0,#weight * 2,5)
    love.graphics.setCanvas()
    love.graphics.setShader()
    -- data collector
    local outp = {}
    local data = level:newImageData()
    local r,g,b,a
    -- display purpeses
    level:setFilter("nearest","nearest")
    love.graphics.setCanvas(screen)
    love.graphics.draw(level,#weight * SIZE + SIZE * 4,SIZE * 2,math.pi * 0.5,SIZE,SIZE) -- display output
    -- collector
    for i=0,#weight - 1,1 do
        r,g,b,a = data:getPixel(i,0)
        print("Data:(" .. i .. "):(" .. r .. "," .. g .. "," .. b .. "," .. a ..")")
        outp[i + 1] = r
    end
    data:release() -- free
    return outp
end
function smallLog(value)
    for k,v in pairs(value) do
        print(k .. "\t|" .. v)
    end
end

function love.load()
    -- setupDisplay-buffer
    screen = love.graphics.newCanvas(800,500)
    -- no Crash?
    cmp:send("bias"   ,love.graphics.newCanvas(1,1,{format="rgba32f"}))
    cmp:send("intake" ,love.graphics.newCanvas(1,1,{format="rgba32f"}))
    cmp:send("weights",love.graphics.newCanvas(1,1,{format="rgba32f"}))
    cmp:send("height" ,1)
    cmp:send("width"  ,1) -- change with normalisation
end

function love.update(dt)
end

function love.draw()
    love.graphics.setCanvas(screen)
    if test then
        -- test
        smallLog(
            sendData({0,.4,.8,.0},{{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},{0,.2,.4,.8},4) -- to test this
        )
        test = false
    end
    -- drawing things
    love.graphics.setCanvas()
    love.graphics.setColor(1,1,1)
    love.graphics.draw(screen,0,0,0,1,1)
end

function love.keypressed( key, scancode, isrepeat )
    if key == "space" then
        test = true
    end
end
function love.mousepressed( x, y, button, istouch, presses )
    -- get Pixel value
    local s = screen:newImageData()
    print(s:getPixel(x,y))
    s:release()
end
The Image is only reading pixel(0|0).
FischBird
Prole
Posts: 4
Joined: Wed Apr 26, 2023 1:42 pm
Location: Germany

Re: Image 2 GPU Error

Post by FischBird »

SOLVED!

I was using Ints to access the Image, it should have been floats!

Changes to the pixel shader: I added "cast to float"

Code: Select all

// uniform vec2 size;
// #define BUFFER_SIZE 100

uniform int height = 1;
uniform int width  = 1;
uniform Image bias;
// x output
uniform Image intake;
// y input
uniform Image weights;
// x is output y is input
// x + M * y


float activation(float value){
    return 1 / (1 + exp(-value));
}
vec3 activation(vec3 value){
    return 1 / (1 + exp(-value));
}

vec4 effect( vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords ){
    int x = int(floor(screen_coords.x));
    int y = 0;
    // vec3 col   = bias[x].rgb;
    // for(;y < height;y++){
    //     col += Texel(intake,vec2(y,0)).rgb * Texel(weights,vec2(x,y)).rgb;
    // }
    // return vec4(activation(col),1);
    float col   = Texel(bias,vec2(float(x) / float(width),0)).r; // added cast to float
    for(;y < height;y++){
        col += Texel(intake,vec2(float(y) / float(height),0)).r * Texel(weights,vec2(float(x) / float(width),float(y) / float(height))).r;
    }
    return vec4( // added cast to float
        activation(col),
        Texel(intake,vec2(float(x) / float(width),0)).r,
        Texel(bias,vec2(float(x) / float(width),0)).r,
        1);
}
Thanks for the support.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 7 guests