Zobrazit předchozí téma :: Zobrazit následující téma |
Autor |
Zpráva |
frca

Založen: 28. 07. 2007 Příspěvky: 1561
|
Zaslal: 4. leden 2024, 00:09:07 Předmět: specialni kopirovani pameti |
|
|
Zdravim, potreboval bych co nejrychleji zkopirovat kazdou ctvrtou polozku ve zdrojovem poli a ulozit je za sebe do ciloveho pole (ktere je 4x mensi). Nebo z jineho uhlu pohledu vzit pole 32-bit intu a nakopirovat ho do 8-bit intu (pricemz 3 bajty se oriznou).
Snazim se dostat linearni framebuffer do 4-strankoveho mode x. Kompilator DJGPP. _________________ www.FRANTICWARE.com |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 4. leden 2024, 05:15:07 Předmět: Re: specialni kopirovani pameti |
|
|
čau,
já bych možná zkusil něco takového (pseudo-kód):
kód: |
constexpr int W = 320;
constexpr int H = 240;
// palettized colors, packed
uint colors[W*H/4];
uint packed[4][W*H/4/4];
inline uint mix_lo(uint a, uint b, uint c, uint d)
{
return
cast byte a | (cast byte b << 8) | (cast byte c << 16) |
cast byte d << 24;
}
void make_compact()
{
for (int dsti : W*H/4/4)
{
int i = dsti << 2;
// x86 addressing: should boil down to dsti*4+offset
auto a = colors[i+0];
auto b = colors[i+1];
auto c = colors[i+2];
auto d = colors[i+3];
packed[0][dsti] = mix_lo(a, b, c, d);
a >>= 8;
b >>= 8;
c >>= 8;
d >>= 8;
packed[1][dsti] = mix_lo(a, b, c, d);
a >>= 8;
b >>= 8;
c >>= 8;
d >>= 8;
packed[2][dsti] = mix_lo(a, b, c, d);
a >>= 8;
b >>= 8;
c >>= 8;
d >>= 8;
packed[3][dsti] = mix_lo(a, b, c, d);
}
}
|
kde colors je tvůj LFB, otázka je, jestli bude mít compiler dost registrů a jak rychlé jsou shifty na 486ce
základní myšlenka: načítat po 32-bitech, pak zabalit a uložit po 32ti bitech do "packed" bufferu v RAM
pak bude stačit jenom nastavit bitplane a zkopírovat z packed bufferů do VGA (klidně po 32-bit, stejně bude záležet akorát na rychlosti a šírce VGA sběrnice)
pokud to je pro 486, asi nevím o žádné magické instrukci, co by to nějak usnadnila, takže bych to asi nechal na překladači (třeba se mu podaří vymyslet i něco chytrého a odstranit nějaké shifty) pokud se ti nechce do inline assembly
chtělo by to vidět disassembly, co gcc vygeneruje, to by mohlo být zajímavé
samozřejmě je možné, že bude rychlejší i naivní způsob, kde se to bude číst po bajtech a lepit do 32-bit wordu a nasype se to rovnou do VGA paměti, bude asi potřeba zprofilovat všechno |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 4. leden 2024, 07:10:02 Předmět: Re: specialni kopirovani pameti |
|
|
tak nakonec se mi to povedlo do inline assembly, je to v msvc a tam nešly použít konstanty, tak tam mám všude natvrdo 320x240:
předpokládám, že všechny buffery jsou globály a nemusí se držet v registrech.
kód: |
void make_compact_asm()
{
__asm
{
// dsti
xor edi, edi
_loop:
lea eax, [edi*8]
mov ebx, [colors + eax + eax + 0*4]
mov ecx, [colors + eax + eax + 1*4]
mov edx, [colors + eax + eax + 2*4]
mov esi, [colors + eax + eax + 3*4]
// mix!
shrd eax, edx, 8
shrd eax, esi, 8
mov al, bl
mov ah, cl
// store packed0
mov [packed + 0*320*240/4 + 4*edi], eax
// shift
shr ebx, 8
shr ecx, 8
shr edx, 8
shr esi, 8
// mix!
shrd eax, edx, 8
shrd eax, esi, 8
mov al, bl
mov ah, cl
// store packed1
mov [packed + 1*320*240/4 + 4*edi], eax
// shift
shr ebx, 8
shr ecx, 8
shr edx, 8
shr esi, 8
// mix!
shrd eax, edx, 8
shrd eax, esi, 8
mov al, bl
mov ah, cl
// store packed2
mov [packed + 2*320*240/4 + 4*edi], eax
// shift
shr ebx, 8
shr ecx, 8
shr edx, 8
shr esi, 8
// mix!
shrd eax, edx, 8
shrd eax, esi, 8
mov al, bl
mov ah, cl
// store packed3
mov [packed + 3*320*240/4 + 4*edi], eax
inc edi
cmp edi, 320*240/4/4
jb _loop
}
}
|
jedna smyčka dělá 16 bajtů (počítám indexovaný režim s 8-bit paletou) |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 4. leden 2024, 07:23:52 Předmět: Re: specialni kopirovani pameti |
|
|
tak ještě jedna verze, shrd je pomalé i na moderních strojích, takže jsem to trochu přeskládal a upravil, tady už to je 1.5x rychlejší než msvc místo pomalejší:
kód: |
void make_compact_asm2()
{
__asm
{
// dsti
xor edi, edi
_loop:
lea eax, [edi*8]
mov ebx, [colors + eax + eax + 0*4]
mov ecx, [colors + eax + eax + 1*4]
mov esi, [colors + eax + eax + 2*4]
mov edx, [colors + eax + eax + 3*4]
// mix!
mov eax, esi
mov ah, dl
shl eax, 16
mov al, bl
mov ah, cl
// store packed0
mov [packed + 0*320*240/4 + 4*edi], eax
// shift
shr esi, 8
shr edx, 8
shr ebx, 8
shr ecx, 8
// mix!
mov eax, esi
mov ah, dl
shl eax, 16
mov al, bl
mov ah, cl
// store packed1
mov [packed + 1*320*240/4 + 4*edi], eax
// shift
shr esi, 8
shr edx, 8
shr ebx, 8
shr ecx, 8
// mix!
mov eax, esi
mov ah, dl
shl eax, 16
mov al, bl
mov ah, cl
// store packed2
mov [packed + 2*320*240/4 + 4*edi], eax
// shift
shr esi, 8
shr edx, 8
shr ebx, 8
shr ecx, 8
// mix!
mov eax, esi
mov ah, dl
shl eax, 16
mov al, bl
mov ah, cl
// store packed3
mov [packed + 3*320*240/4 + 4*edi], eax
inc edi
cmp edi, 320*240/4/4
jb _loop
}
}
|
|
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 4. leden 2024, 09:09:17 Předmět: Re: specialni kopirovani pameti |
|
|
ještě jsem si uvědomil, že můžu ušetřit 6 shiftů...
kód: |
void make_compact_asm3()
{
__asm
{
// dsti*4
xor edi, edi
_loop :
mov ebx, [colors + 4*edi + 0 * 4]
mov ecx, [colors + 4*edi + 1 * 4]
mov esi, [colors + 4*edi + 2 * 4]
mov edx, [colors + 4*edi + 3 * 4]
// mix!
mov eax, esi
mov ah, dl
shl eax, 16
mov al, bl
mov ah, cl
// store packed0
mov[packed + 0 * 320 * 240 / 4 + edi], eax
// shift
shr esi, 8
// mix!
mov eax, esi
mov ah, dh
shl eax, 16
mov al, bh
mov ah, ch
// store packed1
mov[packed + 1 * 320 * 240 / 4 + edi], eax
// shift
shr esi, 8
shr edx, 16
shr ebx, 16
shr ecx, 16
// mix!
mov eax, esi
mov ah, dl
shl eax, 16
mov al, bl
mov ah, cl
// store packed2
mov[packed + 2 * 320 * 240 / 4 + edi], eax
// shift
shr esi, 8
// mix!
mov eax, esi
mov ah, dh
shl eax, 16
mov al, bh
mov ah, ch
// store packed3
mov[packed + 3 * 320 * 240 / 4 + edi], eax
add edi, 4
cmp edi, 320 * 240 / 4
jb _loop
}
}
|
|
|
Návrat nahoru |
|
 |
frca

Založen: 28. 07. 2007 Příspěvky: 1561
|
Zaslal: 4. leden 2024, 19:28:54 Předmět: |
|
|
Díky moc, kopírování po 32 bitech mě nějak nenapadlo.
Alignment adres u 486 a starších nehraje roli, jestli se nepletu...? _________________ www.FRANTICWARE.com |
|
Návrat nahoru |
|
 |
frca

Založen: 28. 07. 2007 Příspěvky: 1561
|
Zaslal: 4. leden 2024, 22:08:00 Předmět: |
|
|
Mám první výsledky z 486tky a hned ten první C++ zdroják jede jak z praku. Pardon za vágní popis performance, zatím nemám přesnější metodu
Edit: asi 12 ms to trvá na 486/66
Ještě chci vyzkoušet to přímé kopírování do VRAM bez mezibufferu.
Edit 2: ^ je o něco pomalejší. _________________ www.FRANTICWARE.com |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 5. leden 2024, 00:50:06 Předmět: |
|
|
super!
jakože přímé kopírování je pomalejší?
jak je na tom 486ka se zarovnáním netuším, ale mohlo by stačit použít alignas
tak ještě tu poslední asm verzi by mě zajímalo docela jak to poběží, jestli jsem ručně schopný na 486ce beatnout gcc
ale přepisovat to do ohavné at&t syntaxe by se mi asi taky moc nechtělo
EDIT: (ještě by to šlo přeložit v netwide assembleru jako position independent a udělat self-modifying code a pak přes nějaký tool to zkonvertovat a includnout v C++ - takhle jsem v robodovi dělal sample mixing rutiny)
EDIT2: udělal jsem to, máš link v PM |
|
Návrat nahoru |
|
 |
frca

Založen: 28. 07. 2007 Příspěvky: 1561
|
Zaslal: 6. leden 2024, 00:04:38 Předmět: |
|
|
Vystup djgpp s -O2 vypada nejak takto. Od oka to je skoro 2x vice radku (a tedy 2x pomalejsi?)
kód: |
_make_compact:
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %esi
movl $_framebuf, %esi
pushl %ebx
xorl %ebx, %ebx
.p2align 4,,7
.p2align 3
L2:
movl (%esi), %ecx
movl 12(%esi), %edi
movl %ecx, %edx
movl 4(%esi), %eax
sall $24, %edi
andl $255, %edx
orl %edi, %edx
movl %eax, %edi
sall $8, %edi
andl $65535, %edi
orl %edi, %edx
movl 8(%esi), %edi
sall $16, %edi
andl $16711680, %edi
orl %edi, %edx
movl %eax, %edi
andl $65280, %edi
movl %edx, _packed(,%ebx,4)
movzbl %ch, %edx
orl %edi, %edx
movl 8(%esi), %edi
sall $8, %edi
andl $16711680, %edi
orl %edi, %edx
movl 12(%esi), %edi
shrl $8, %edi
sall $24, %edi
orl %edi, %edx
movl 8(%esi), %edi
movl %edx, _packed+19200(,%ebx,4)
movl %ecx, %edx
shrl $16, %edx
andl $16711680, %edi
andl $255, %edx
orl %edi, %edx
movl %eax, %edi
shrl $8, %edi
shrl $24, %eax
andl $65280, %edi
sall $8, %eax
orl %edi, %edx
movl 12(%esi), %edi
shrl $16, %edi
sall $24, %edi
orl %edi, %edx
movl %edx, _packed+38400(,%ebx,4)
movl 8(%esi), %edx
shrl $24, %edx
sall $16, %edx
orl %edx, %eax
movl 12(%esi), %edx
shrl $24, %ecx
andl $-16777216, %edx
orl %edx, %ecx
addl $16, %esi
orl %ecx, %eax
movl %eax, _packed+57600(,%ebx,4)
incl %ebx
cmpl $4800, %ebx
jne L2
popl %ebx
popl %esi
popl %edi
popl %ebp
ret |
_________________ www.FRANTICWARE.com |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 6. leden 2024, 00:22:34 Předmět: |
|
|
no to by se muselo zprofilovat, z hlavy nodhadnu. každá instrukce trvá jinak, u mě většina jsou movy
gcc kód má 60 instrukcí a 15 shiftů, můj optimalizovaný v assembly 37 a 10 shiftů, nevím co s tím udělá instruction scheduling/pipeline dependence na tom stroji, ale chtělo by to porovnat
kód co vygeneroval gcc nevypadá špatně, ale vsadil bych si že ten můj by mohl být i rychlejší - pokud ti jde primárně o výkon, což počítám, že na 486ce ano
přepsat do inline assembly by neměl být problém, když už z něj. důvodu nechceš integrovat ten self-modifying kód, co jsem posílal (pokud ti jde o portování do budoucna, dalo by se to přece ifdefnout) |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 6. leden 2024, 23:05:45 Předmět: Re: specialni kopirovani pameti |
|
|
v návaznosti na PM: tady je to přepsané do at&t syntaxe,
ještě by to nějak chtělo pořešit konstanty (320*240 natvrdo), ale to už nebudu řešit:
kód: |
#include <cstdint>
#include <cstdio>
using uint = uint32_t;
using byte = uint8_t;
constexpr int W = 320;
constexpr int H = 240;
// palettized colors, packed
uint colors[W*H/4];
uint packed[4][W*H/4/4];
inline uint mix_lo(uint a, uint b, uint c, uint d)
{
return (byte)a | (byte)b << 8 | (byte) c << 16 | (byte)d << 24;
}
void make_compact()
{
for (int dsti=0; dsti<W*H/4/4; dsti++)
{
int i = dsti << 2;
auto a = colors[i+0];
auto b = colors[i+1];
auto c = colors[i+2];
auto d = colors[i+3];
packed[0][dsti] = mix_lo(a, b, c, d);
a >>= 8;
b >>= 8;
c >>= 8;
d >>= 8;
packed[1][dsti] = mix_lo(a, b, c, d);
a >>= 8;
b >>= 8;
c >>= 8;
d >>= 8;
packed[2][dsti] = mix_lo(a, b, c, d);
a >>= 8;
b >>= 8;
c >>= 8;
d >>= 8;
packed[3][dsti] = mix_lo(a, b, c, d);
}
}
void make_compact_asm3()
{
asm volatile(
//dsti*4
"xor %%edi, %%edi\n"
"_loop:\n"
// stupid at&t: disp(base, index, scale)
"mov %0+0*4(,%%edi,4), %%ebx\n"
"mov %0+1*4(,%%edi,4), %%ecx\n"
"mov %0+2*4(,%%edi,4), %%esi\n"
"mov %0+3*4(,%%edi,4), %%edx\n"
// mix!
"mov %%esi, %%eax\n"
"mov %%dl, %%ah\n"
"shl $16, %%eax\n"
"mov %%bl, %%al\n"
"mov %%cl, %%ah\n"
// store packed0
"mov %%eax, %1+0*320*240/4(%%edi)\n"
// shift
"shr $8, %%esi\n"
// mix!
"mov %%esi, %%eax\n"
"mov %%dh, %%ah\n"
"shl $16, %%eax\n"
"mov %%bh, %%al\n"
"mov %%ch, %%ah\n"
// store packed1
"mov %%eax, %1+1*320*240/4(%%edi)\n"
// shift
"shr $8, %%esi\n"
"shr $16, %%edx\n"
"shr $16, %%ebx\n"
"shr $16, %%ecx\n"
// mix!
"mov %%esi, %%eax\n"
"mov %%dl, %%ah\n"
"shl $16, %%eax\n"
"mov %%bl, %%al\n"
"mov %%cl, %%ah\n"
// store packed2
"mov %%eax, %1+2*320*240/4(%%edi)\n"
//shift
"shr $8, %%esi\n"
// mix!
"mov %%esi, %%eax\n"
"mov %%dh, %%ah\n"
"shl $16, %%eax\n"
"mov %%bh, %%al\n"
"mov %%ch, %%ah\n"
// store packed3
"mov %%eax, %1+3*320*240/4(%%edi)\n"
"add $4, %%edi\n"
"cmp $320*240/4, %%edi\n"
"jb _loop\n"
:
: "m"(colors), "m"(packed)
: "cc", "%eax", "%ebx" ,"%ecx", "%edx", "%esi", "%edi"
);
}
int main()
{
colors[0] = 0xfacebabeu;
colors[1] = 0x11111111u;
colors[2] = 0x22222222u;
colors[3] = 0x33333333u;
make_compact_asm3();
printf("p0: %08x\n", (unsigned)packed[0][0]);
printf("p1: %08x\n", (unsigned)packed[1][0]);
printf("p2: %08x\n", (unsigned)packed[2][0]);
printf("p3: %08x\n", (unsigned)packed[3][0]);
return 0;
}
|
|
|
Návrat nahoru |
|
 |
frca

Založen: 28. 07. 2007 Příspěvky: 1561
|
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 7. leden 2024, 03:05:28 Předmět: |
|
|
super! dal jsem ti lajk, nicméně bych doporučil následující změnu (s tím už bych to klidně použil i v nějakém svém projektu, i když DOS věci mě aktuálně nelákají a HVDOSDEV loni stejně nebyl):
1) jestli čekat na vsync dát jako parametr, ne jako define, je to pružnější a užitečnější
2) používat 3 stránky místo dvou (latence bude stejná, ale umožní to fígl, co popíšu dole)
kód: |
void framebuf_flip(uint8_t* VGA, int vsync)
{
make_compact_asm3();
static int page = 0;
int target_page = page + 1;
if (target_page > 2)
target_page = 0;
VGA += target_page * 19200;
outp(SC_INDEX, MAP_MASK);
outp(SC_DATA, 1);
memcpy(VGA, packed[0], 19200);
outp(SC_INDEX, MAP_MASK);
outp(SC_DATA, 2);
memcpy(VGA, packed[1], 19200);
outp(SC_INDEX, MAP_MASK);
outp(SC_DATA, 4);
memcpy(VGA, packed[2], 19200);
outp(SC_INDEX, MAP_MASK);
outp(SC_DATA, 8);
memcpy(VGA, packed[3], 19200);
while (vsync && (inportb(SR) & VRETRACE))
;
outpw(CRTC_INDEX, 0x000c | (0x4b00u*target_page));
// note: could be coupled with xmode init to save this outpw
outpw(CRTC_INDEX, 0x000d);
while (vsync && !(inportb(SR) & VRETRACE))
;
page = target_page;
}
|
proč použít 3 stránky místo dvou:
registr page start (0xc) se latchuje VGA hardwarem, tzn. překlopí se automaticky tak, aby to netearovalo někde na začátku nebo konci vsyncu, přesně nevím
můžeš teď renderovat bez vsyncu a bez tearingu na různých fps, pokud to je míň než 120, pak by se efekt rozbil, tzn. vyžaduje to ještě framelimiter řekněme na 100 fps
používal jsem to v robodovi, bylo to dobré, protože když nestíháš 60 tak s vsyncem máš najednou 30, když nestíháš 30 máš 20, pak 15 atd., s tímhle to je plynulejší
jediné co je bez vsyncu blbé je, že s tím nebudou fungovat dyn. změny palety (bude to sněžit) - proto je fajn ovládat vsync v runtime (např. v menu animace palety, ingame ne)
EDIT: možná by bylo elegantnější místo vsync boolu tam předávat obecně flagy a tam by mohlo být třeba jestli použít VSYNC, jestli použít 3 stránky apod., ale to už není tak důležité |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 7. leden 2024, 04:40:39 Předmět: |
|
|
hmm, ještě bys mohl pls zkusit nahradit memcpy a memset za tyhle moje rutiny, používal jsem to v robodovi.
rád bych věděl, jestli to na reálné 486ce něco dá
defaultní memcpy co generuje djgpp s tím novým gcc je rep movsd, což je na moderním hw cool, ale nejsem si jistý, že to bude stejně cool i na 486
pro memset dokonce používá rep stosb, kde už přesvědčený nejsem vůbec.
samozřejmě - správná věc na moderním stroji, ale...
kód: |
#ifndef MEMFAST_H
#define MEMFAST_H
#include <stddef.h>
inline void memset_fast(void *dst, int fill, int size)
{
// unpack filler
uint32_t ufill = uint8_t(fill);
ufill |= ufill << 8;
ufill |= ufill << 16;
uint32_t *udst = (uint32_t *)dst;
while (size >= 4*8)
{
udst[0] = ufill;
udst[1] = ufill;
udst[2] = ufill;
udst[3] = ufill;
udst[4] = ufill;
udst[5] = ufill;
udst[6] = ufill;
udst[7] = ufill;
udst += 8;
size -= 4*8;
}
while (size >= 4)
{
*udst++ = ufill;
size -= 4;
}
uint8_t *bdst = (uint8_t *)udst;
while (size-- > 0)
*bdst++ = (uint8_t)ufill;
}
inline void memcpy_fast(void *dst, const void *src, int size)
{
uint32_t *udst = (uint32_t *)dst;
const uint32_t *usrc = (const uint32_t *)src;
while (size >= 4*4)
{
uint32_t a, b, c, d;
a = usrc[0];
b = usrc[1];
c = usrc[2];
d = usrc[3];
udst[0] = a;
udst[1] = b;
udst[2] = c;
udst[3] = d;
usrc += 4;
udst += 4;
size -= 4*4;
}
while (size >= 4)
{
*udst++ = *usrc++;
size -= 4;
}
uint8_t *bdst = (uint8_t *)udst;
const uint8_t *bsrc = (uint8_t *)usrc;
while (size-- > 0)
*bdst++ = *bsrc++;
}
#endif
|
EDIT: co se koukám na ref. manuál, tak rep movsd je 12 + 3*ecx cyklů,
co jsem koukal na kód co vygeneroval gcc pro memcpy_fast tak tam by to neměl být velký rozdíl (chtělo by to změřit), ale memset_fast by měl být výrazně rychlejší, než rep stosb, který je 7+4*ecx cyklů (i kdyby to byl rep stosd, tak by měl být memset_fast rychlejší)
ten memcpy_fast by se dal ještě koukám rozrolovat víc, pokud by to stálo vůbec za to |
|
Návrat nahoru |
|
 |
frca

Založen: 28. 07. 2007 Příspěvky: 1561
|
Zaslal: 7. leden 2024, 08:36:17 Předmět: |
|
|
Místo pro výměnu palety je těsně po novém framebuf_flip s vsync?
Dále, hry časuji typicky s timerem konfigurovaným na 60 Hz, takže teď by to bylo 120 Hz - to by ještě s třístránkovou metodou fungovalo? Pak by byl limiter triviální:
timer_interrupt()
{
++timer_count;
}
...
// game loop
{
while (timer_count == prev_timer_count); // wait for timer
... // game step, rendering
framebuf_flip(VGA, 0);
}
Edit: Vlastně timer/limiter na 60 Hz bude podle mě stačit. Na rychlém HW se využijí všechny frejmy módu X bez artefaktů a na pomalém VSYNC nebude nic brzdit.
Tyhle dotazy píšu pro ujištění, protože to budu i nějak dokumentovat.
Edit2: 3stránkový page flip funguje na 486 bezvadně. Je to stejně rychlé jako ten původní, ale bez artefaktů. _________________ www.FRANTICWARE.com |
|
Návrat nahoru |
|
 |
|