|
|
|
|
|
|
|
|
|
|
What's News in the PlayBasic world ? - Find out here.
PlayBasic V1.64O - WIP Round Up #5
|
By: Kevin Picone Added: July 10th, 2013
|
Category: All,Upgrade,Beta Testing,Resource Binding,Palette Mapping
|
The following are just excerpts taken from the current PlayBasic V1.64O Work in Program blog between June->July 2013.
PlayBasic V1.64O Beta 13 - 32bit Power Of 2 and Black Optimizing
Been taking a peek back into the texture map span rendering library, which is the core behind pretty much anything that's rotated. Can't remember now, but at some point we added a hand full of short cut routines for some of common render modes. So if the draw mode was 'simple' we call a dedicated routine to solve this. This avoids having to falling through the multi pass filler, which is pretty flexible, but can be eat up some render time.
Rotated Sprites are texture mapped quads in PlayBasic, when we draw a rotated image/texture map, we're fetching pixels from texture in a none linear fashion. So we look up each texel (pixel in texture space) by interpolation down the polygon edges and across the strip. The coordinates might be anywhere in the texture though. Now since the texture can be any size, we need a multiply in the texel fetch to work out the where in memory the pixel actually is. Knowing this, we added some power of two render modes back during some other update. Now when the texture mapper routine knows the source texture is a power of two (width), it calls a version of the filler than uses bit shifts rather than mults. A bit shift is simply faster !
What this means is that if you're using image widths that are a nice round power of two, you'll get some extra speed for pretty much nothing. Not only that, if the mask colour is zero (black = argb(0,0,0,0)), there's some combinations of the span filler that use this knowledge to it's advantage. Since this allows the inner fill routine to avoid actually comparing texels with a user defined mask colour. Winning us back some extra through put.
OK.. This is all old news, so tonight's little challenge has been to try and peel back the 32bit routines as much as possible with some interesting results. This morning build of Beta 13 is rendering s the 500 (64*64) sprite test scene around 20% faster than V164O beta 12 (and all older builds) when the image is power of two and mask colour is zero. Not only that, it's actually slightly faster across the board.
Here's the results from the bench mark.
CODE:
--------------------------------------------------
Version:PlayBasic V1.64O Beta 12
Date:05 Jun 2013
Image:gfx/ship.bmp
--------------------------------------------------
Test:1
Sprites[500] Rotated / Transparent=ON
Image=64*64 po2=1 MaskCOlour=0
Ticks=61250.1 Over=100 tests
Test:2
Sprites[500] Rotated / Transparent=OFF
Image=64*64 po2=1 MaskCOlour=0
Ticks=59431.68 Over=100 tests
Test:3
Sprites[500] Rotated / Transparent=ON
Image=64*64 po2=1 MaskCOlour=16711935
Ticks=59031.71 Over=100 tests
Test:4
Sprites[500] Rotated / Transparent=OFF
Image=64*64 po2=1 MaskCOlour=16711935
Ticks=58863.04 Over=100 tests
Test:5
Sprites[500] Rotated / Transparent=ON
Image=65*65 po2=0 MaskCOlour=0
Ticks=65830.69 Over=100 tests
Test:6
Sprites[500] Rotated / Transparent=OFF
Image=65*65 po2=0 MaskCOlour=0
Ticks=64011.32 Over=100 tests
Test:7
Sprites[500] Rotated / Transparent=ON
Image=65*65 po2=0 MaskCOlour=16711935
Ticks=71085.37 Over=100 tests
Test:8
Sprites[500] Rotated / Transparent=OFF
Image=65*65 po2=0 MaskCOlour=16711935
Ticks=64103.31 Over=100 tests
--------------------------------------------------
Version:PlayBasic V1.64O Beta 13
Date:05 Jun 2013
Image:gfx/ship.bmp
--------------------------------------------------
Test:1
Sprites[500] Rotated / Transparent=ON
Image=64*64 po2=1 MaskCOlour=0
Ticks=48223.4 Over=100 tests
Test:2
Sprites[500] Rotated / Transparent=OFF
Image=64*64 po2=1 MaskCOlour=0
Ticks=58496.33 Over=100 tests
Test:3
Sprites[500] Rotated / Transparent=ON
Image=64*64 po2=1 MaskCOlour=16711935
Ticks=48894.36 Over=100 tests
Test:4
Sprites[500] Rotated / Transparent=OFF
Image=64*64 po2=1 MaskCOlour=16711935
Ticks=58264.08 Over=100 tests
Test:5
Sprites[500] Rotated / Transparent=ON
Image=65*65 po2=0 MaskCOlour=0
Ticks=64852.79 Over=100 tests
Test:6
Sprites[500] Rotated / Transparent=OFF
Image=65*65 po2=0 MaskCOlour=0
Ticks=62616.84 Over=100 tests
Test:7
Sprites[500] Rotated / Transparent=ON
Image=65*65 po2=0 MaskCOlour=16711935
Ticks=67575.52 Over=100 tests
Test:8
Sprites[500] Rotated / Transparent=OFF
Image=65*65 po2=0 MaskCOlour=16711935
Ticks=62693.11 Over=100 tests
If you look closely you'll notice there's a pretty handy reduction in the first four tests when running in Beta13, which all come from the maskcolour and width of the texture is a power of two. There's not much change in the general routine though (there is some). Since $ff00ff is a commonly used mask, it might be viable to roll a few variations with that colour also. Can't be sure, but I'd imagine they'd be somewhere in the middle.
PlayBasic V1.64O Beta 14 - Palette Mapping Library
Earlier this update we looked at some methods for mapping groups of 8bits out as palette mapped pixels. To test the theory, I posted an example that showed how we can draw mock up commodore 64 screen in 'hires' mode. But the process is not just the realm of emulation, as such conversions also occur loading / importing image data, 2bit & 4bit windows bitmap files come to mind.
Since the initial tinker, i've been thinking of dropping some similar functions into a SLIB. So far there's only four functions, but they cater for the situations that come to mind. So we've got a function to take a byte and couple of functions to render splits of bytes/words as palettes mapped horizontal strip. There's not rect render as this point, but it's similar to something I was testing a number of years ago.
For those not sure what palette mapping is, well it's a bit of step back to the old days of 8bit graphics hardware. A classic 8Bit screen mode, has up to 256 unique colours on screen ( 2^8=256 ) . Most systems that had 8bit displays had 24bit RGB colours, much like today. The difference is that rather than the screen representation being an array colours RGB values like it is today, legacy hardware used a palette indexes. So each pixel was a index into the palette array. Each pixel index was one byte.
You can visual it like this,
Dim Palette(256)
Dim Screen(320,200)
So the hardware would read through the Screen() array, grab the each colour index value and use that value to read from the Palette to get output colour. ie OutputRGB =Palette( Screen(Xpos,Ypos))
Old systems used palette mapped images because they're very efficient (on memory and cpu). For example, to draw graphics in 8bit, the program only has to shift the colour index data in screen(), which in an 8bit screen mode is 1/4 of we have push around today in 32bit modes. The down side is that you're very limited in terms of colours. You literally only have 256 colours to choose from on screen at once and it's difficult to remap/make them at runtime. Some systems had ways around this (mutli plexing), but it's still pretty limiting.
However, there's a number of neat effects you can do with palettes, probably the one most people are familiar with is colour cycling. Which you'd often see used to convey motion in water/lava sprites/pictures in retro games. Ironically, now days we'd pretty much have to build an animation to achieve to the same thing.
Knowing this, figured one easy way would be to use a function that's designed to take 8bit/16bit arrays and render them as if they're a normal strip/image. So if you had a colour cycling fragment, you'd scroll the palette, then render the fragment to a regular PB image and hey presto colour cycling.
Here's an example of a routine to draw a palette mapped set of strips to an image. The 8bit image data is just set up as runs of ascending values with the palette set up to scale up from, rgb(0,0,0) to rgb(255,255,255), so we get a gradient of sorts. I've randomly changed some of the pixels and the first few colours to make sure it's rendering correctly.
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
Test_Palette_Mapped_Strips: // (8bit palette map)
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
rendertoimage screen
Lockbuffer
ThisRgb=Point(0,0)
Ypos=0
For ypos =0 to 255
address=GetBankPtr(ScreenBank)+(ypos*320)
DrawPaletteMappedStrip8(xpos,ypos,Address,320,$ff)
next
unLockbuffer
return
[idnent] Speed wise it's fine, the loop executes in about 1/2 a millisecond on my old desk top, attached is a picture. Was going to create some a little more interest to look at but couldn't be bothered..
PlayBasic V1.64O Beta 14 - Palette Mapping Library Cont
Ok.. so dropped some more functions into the palette mapping library and back end. This is going to be pretty difficult to wrap your minds around, but in the demo bellow we're loading RGB mapped images, converted them to a global palette which is 16bit in size. This gives us a 2^16 possible colours that be used. The colours can still be 32bit. Now since we're using 16bit palette, we need to force the loaded images to be 16bit 656 format. We do this using the CReateFXimagfeEX command. Which allows you to create an image of any depth (pixel format), regardless of the screen mode. Then a conversion routine replaces the RGB colour with the palette indexes in the image.
Now if we create a 'screen' image that's the same depth as the palette mapped sprites (16bit), we can use the normal image rendering stuff in PlayBasic to draw them to the conceptually palette mapped buffer. Once we're done rendering, we draw the screen out as a group of palette mapped strips. This gives us a 32bit end result, but we've halved the amount of bandwidth that's moved around in the picture. Plus you can modify the palette. Raster Bars, Colour cycling etc
Here's a demo showing the basic approach, the palette mapped scene is actually fading in/out in this demo. Fading a Palette mapped mode is much less work, since we only have to scale the palette RGB's and not every single pixel.
#include "PaletteMapping"
loadFont "verdana",2,24,0,8
Dim Palette($10000)
Dim MasterPalette($10000)
SetPalette(Palette())
; set colour zero as RGB(0,0,0), since the images use that
; colour as transparent
Palette(0) = $00000000
ColourCount=1
; create 16bit fX image as the palette mapped screen..
Screen=CreateScreen(800,600)
; load some images and remap the RGBS to palette indexes
Ship,ColourCount= LoadAsPaletteMappedImage("gfx\ship.bmp",_
Palette(),ColourCount)
Bubble,ColourCount= LoadAsPaletteMappedImage("gfx\bubble_64x64.bmp",_
Palette(),ColourCount)
Logo,ColourCount= LoadAsPaletteMappedImage("gfx\uwlogo.bmp",_
Palette(),ColourCount)
// Make a copy of the original palette
CopyArray Palette(),MasterPalette()
Do
cls 255
frames++
setcursor 0,0
t=timer()
// --------------------------------------------
// draw to the 16bit screen
// --------------------------------------------
rendertoimage Screen
cls 0
gridimage Ship,mousex()-250,_
mousey()-250,10,10,true
drawrotatedimage Bubble,400,100,_
angle#,2,2,-32,-32,true
angle#=wrapangle(angle#+2)
drawimage logo,100,200,true
rendertoscreen
// draw the palette mapped buffer
// to the actual screen
RenderScreen(Screen,Xpos,Ypos,Palette())
tt#+=Timer()-t
// -----------------------------------------------
// draw what the sprites to
// -----------------------------------------------
drawimage ship,0,500,true
drawimage bubble,64,500,true
setfont 2
lockbuffer
print "Screen Resolution:"+Str$(GetImageWidth(Screen))_
+"*"+Str$(GetImageHeight(Screen))
print "Palette Size:"+str$(ColourCount)
print "Ticks:"+str$(tt#/frames)
print "Fps:"+str$(fps())
unlockbuffer
// --------------------------------------
// Fade palette in and out
// --------------------------------------
ScaleLevel=128+Cos(angle#)*128
ScaleLevel=ClipRange(ScaleLevel,0,255)
For lp=0 to ColourCount
ThisRgb=MasterPalette(lp)
Palette(lp)=RgbScale(ThisRgb,ScaleLevel)
next
Sync
loop
Psub CreateScreen(Width,Height)
; the
local ThisIMage=GetFreeImage()
CreateFXImageEx ThisImage,Width,Height,16
EndPsub ThisImage
Psub RenderScreen(Screen,Xpos,Ypos,Palette())
if GetImageStatus(Screen) and Screen>0
if GetImageDepth(Screen)=16
SetPalette(Palette())
oldSurface=GetSurface()
rendertoimage Screen
lockbuffer
thisrgb=point(0,0)
Width =GetImageWidth(Screen)
HEight=GetImageHeight(Screen)
Ptr =GetImageptr(Screen)
Modulo=GetImagePitch(Screen)
unlockbuffer
rendertoimage OldSurface
lockbuffer
thisrgb=point(0,0)
For ypos =0 to Height-1
DrawPaletteMappedStrip16(0,ypos,Ptr,Width,$ffff)
ptr+=Modulo
next
unLockbuffer
endif
endif
EndPsub
Psub LoadAsPaletteMappedImage(File$,Palette(),ColourCount)
local TempImage=LoadNewImage(File$,2)
local w=GetIMageWidth(TempImage)
local h=GetIMageHeight(TempImage)
local ThisImage=GetFreeImage()
CreatefxImageEx ThisImage,w,h,16
ColourCount=PaletteMapRGBImage(TempImage,Palette(),ColourCount)
copyrect TEmpImage,0,0,w,h,ThisImage,0,0
deleteimage tempimage
EndPsub ThisImage,COlourCount
So bellow we have a shot of what this looks like running.. In the bottom left hand corner you can see the palette mapped SHIP/BUBBLE images drawn directly to screen. The pixel data in them is no longer RGB's colour values, but INDEX into the palette array. So they look all crazy now when drawn to to the normal display..
PlayBasic V1.64O - Resource Binding
Been stewing on this for a few weeks now and aren't really any closer to an ideal situation. My preferred solution would see minimal changes to user code when the 'media' is bound into the final exe or loaded from disc. So a line code like loadimage "gfx\MyPicture.png",1 would work regardless of the context. If the media is bound into the EXE then the loader process and find this file as an internal resource and load it from memory, if it's not, it attempts to load it from disc. Which is about as simple as I can make it.
But.... there's a but, which comes down to how the programmer tells PlayBasic to construct the internal binding. It can't work this from the user code, since anything that's not an literal is virtually impossible to compute at compile time.
Ie.
For frame=1 to 10
LoadIMage "gfx\ship"+str$(frame)+".bmp", 100+frame
next
With some messing around you could solve that at compile time, but all it takes is one external unknown and your toast. So it becomes necessary to tell the compiler what files to bind and what not to bind into the exe. The lazy solution is to have recursive include function, that will grab everything in the folder and attached it. That's a pretty naive solution as users would doubt end with distributing files they had never intended.
There needs to be some flexibility in the approach as binding isn't necessarily just for image files, data files could also be attached. Those would have to be handled differently for the user than just a loadimage command, so it introduces another little twist into the process. It's not uncommon for IDE's to have a 'resource managers' built into them today. Obviously we can't do that today with PlayWrite, but you could have a tool that does something like that. So rather than binding the 'files + folders' by hand, you just attach the previously created pack file.
Something to think about anyway.
PlayBasic V1.64O Beta 17 - Resource Binding Library
So another session on and we're a bare bones implementation of data binding. To handle the binding we use the #AddResource directive followed by a literal string containing the file name (and path). It's pretty dumb as this point as it only allows files to be bound individually. This occurs during compile time currently so any time it sees the #AddResource directive it loads that media into memory. The load is loaded as is, it doesn't understand the file, it's just being considered as pure data.
Binding is one thing, using them other problem. To help out, i'm throwing together some high level functions to grab/copy resources in particular way. These will most likely be a as string, as bank, or as image if it's a image media type. To query a resource we use it's imported filename. This is a bit iffy, but it fits perfectly into how this embeds, so ya get that.
The example code bellow, imports a text file into the program memory (and the final exe when built). To access the resource data in our program we use LoadResourceAsString$ function. The query functions use the index of the resource, rather than the name. There's FindResourceIndex(Name$) to find the index of the media in the table.
Example Usage:
Constant TextFile$="Files/Hello-World.txt"
#AddResource TextFile$
; Find this resources index , indexes range between
; zero and number of bound resources in the program
index=FindResourceIndex(TextFile$)
if index>-1
; load the text file resource as string
s$=LoadResourceAsString$(Index)
; split the string into this array
dim rows$(0)
s$=replace$(s$,chr$(13),"")
count=splittoarray(S$,chr$(10),Rows$())
; dump it to the screen
for lp =0 to Count
print rows$(lp)
next
endif
Sync
WaitKey
Example Text File:
CODE:
Hello World
This is some text that's been loaded into the app and treated as an in memory resource.
This is some text that's been loaded into the app and treated as an in memory resource.
End of this file is here
---------------------------------------------------------------------------------------
PlayBasic V1.64O Beta 17 - Resource Binding Library #2
Dropped a few more test functions last night, was going to add them all as internal bindings, but might split them in two and have the core stuff bound and the helper functions as a library. Since the helpers are extra bits and aren't always needed. So far we're got some functions that will load bound materials in as PB media as images and fonts. Since you can do this manually you can modify the resource before you use it. Which might be some form of simple decompression, decryption etc etc.
In this example we're binding the a text file, PNG and CRF font into EXE memory and then loading them directly on demand, without ever needing to save them out to disc.
Constant TextFile$="Files/Hello-World.txt"
#AddResource TextFile$
#AddResource "Files/Uwlogo.png"
#AddResource "Files/Arial36.crf"
; Find this resources index , indexes range between zero
; and number of bound resources in the program
index=FindResourceIndex(TextFile$)
if index>-1
; load the text file resource as string
s$=LoadResourceAsString$(Index)
; split the string and remove chr 13's
dim rows$(0)
s$=replace$(s$,chr$(13),"")
count=splittoarray(S$,chr$(10),Rows$())
; dump it to the screen
for lp =0 to Count
print rows$(lp)
next
ThisBank=LoadResourceAsBank(Index)
Print ThisBank
For lp =0 to GetBankSize(ThisBank)-1
print PeekBankByte(ThisBank,lp)
#print chr$(PeekBankByte(ThisBank,lp))
next
endif
ThisImage=LoadResourceImage("Files/Uwlogo.png")
drawimage ThisIMage,400,300,false
ThisFont =LoadResourceFont("Files/Arial36.crf")
setfont ThisFont
text 200,200,"YEAH"
Sync
WaitKey
Today's little task will be to hook into the LoadFont + LoadImage commands so they can find attached resources and automatically load them for memory for you. The frame work exists now so it should be fairly easy.. Should be.. but you never know.
Edit #1 - Load Font Resources
So the first step is up and running. If you bind a CRF into the exe, the load functions will load it from the memory for you.
// This directive imports the file data into memory/exe at compile time
#AddResource "Files/Arial36.crf"
// The load statement check if this file is actually a
// resource, if so it loads it from memory
Arial36=loadNewFont("Files/Arial36.crf",1)
setfont Arial36
text 200,300,"Loading directly from resource:"
yes, it's that easy !
Edit #2 - Load Image Resources (BMP/PNG/TGA)
The second step is up and running also. So if you bind a PNG,BMP,TGA image those images can be loaded directly from the resource buffer in memory. They never have to be extracted to disc.
// This directive imports the file data into memory/exe at
// compile time
#AddResource "Files/uwlogo.png"
// The load statement checks if this file is actually a resource,
// if so, it loads it from memory
logo=loadNewImage("Files/uwlogo.png",1)
DrawImage Logo,200,300,true
Sync
waitkey
READ BLOG for pictures/sources codes and beta downloads
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|