plan9fox/sys/src/9/pc/screen.c
cinap_lenrek 3045d63969 reduce software cursor flickering
the software cursor starts flickering and reacts bumby if a process
spends most of its time with drawlock acquired because the timer interrupt
thats supposed to redraw the cursor fails to acquire the lock at the time
the timer fires.

instead of trying to draw the cursor on the screen from a timer interrupt
30 times per second, devmouse now creates a process calling cursoron() and
cursoroff() when the cursor needs to be redrawn. this allows the swcursor
to schedule a redraw while holding the drawlock in swcursoravoid() and
cursoron()/cursoroff() are now able to wait for a qlock (drawlock) because
they get called from process context.

the overall responsiveness is also improved with this change as the cursor
redraw rate isnt limited to 30 times a second anymore.
2013-04-14 16:28:54 +02:00

568 lines
10 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "../port/error.h"
#define Image IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"
#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19)
Point ZP = {0, 0};
Rectangle physgscreenr;
Memimage *gscreen;
VGAscr vgascreen[1];
Cursor arrow = {
{ -1, -1 },
{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
},
{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
},
};
int
screensize(int x, int y, int, ulong chan)
{
VGAscr *scr;
qlock(&drawlock);
if(waserror()){
qunlock(&drawlock);
nexterror();
}
memimageinit();
lock(&vgascreenlock);
if(waserror()){
unlock(&vgascreenlock);
nexterror();
}
scr = &vgascreen[0];
scr->gscreendata = nil;
scr->gscreen = nil;
if(gscreen){
freememimage(gscreen);
gscreen = nil;
}
if(scr->paddr == 0){
if(scr->dev && scr->dev->page){
scr->vaddr = KADDR(VGAMEM());
scr->apsize = 1<<16;
}
scr->softscreen = 1;
}
if(scr->softscreen){
gscreen = allocmemimage(Rect(0,0,x,y), chan);
scr->useflush = 1;
}else{
static Memdata md;
md.ref = 1;
if((md.bdata = scr->vaddr) == 0)
error("framebuffer not maped");
gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
scr->useflush = scr->dev && scr->dev->flush;
}
if(gscreen == nil)
error("no memory for vga memimage");
scr->palettedepth = 6; /* default */
scr->memdefont = getmemdefont();
scr->gscreen = gscreen;
scr->gscreendata = gscreen->data;
physgscreenr = gscreen->r;
vgaimageinit(chan);
unlock(&vgascreenlock);
poperror();
drawcmap();
swcursorinit();
qunlock(&drawlock);
poperror();
return 0;
}
int
screenaperture(int size, int align)
{
VGAscr *scr;
scr = &vgascreen[0];
if(scr->paddr) /* set up during enable */
return 0;
if(size == 0)
return 0;
if(scr->dev && scr->dev->linear){
scr->dev->linear(scr, size, align);
return 0;
}
/*
* Need to allocate some physical address space.
* The driver will tell the card to use it.
*/
size = PGROUND(size);
scr->paddr = upaalloc(size, align);
if(scr->paddr == 0)
return -1;
scr->vaddr = vmap(scr->paddr, size);
if(scr->vaddr == nil)
return -1;
scr->apsize = size;
return 0;
}
uchar*
attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen)
{
VGAscr *scr;
scr = &vgascreen[0];
if(scr->gscreen == nil || scr->gscreendata == nil)
return nil;
*r = scr->gscreen->clipr;
*chan = scr->gscreen->chan;
*d = scr->gscreen->depth;
*width = scr->gscreen->width;
if(scr->gscreendata->allocd){
/*
* we use a memimage as softscreen. devdraw will create its own
* screen image on the backing store of that image. when our gscreen
* and devdraws screenimage gets freed, the imagedata will
* be released.
*/
*softscreen = 0xa110c;
scr->gscreendata->ref++;
} else
*softscreen = scr->useflush ? 1 : 0;
return scr->gscreendata->bdata;
}
void
flushmemscreen(Rectangle r)
{
VGAscr *scr;
uchar *sp, *disp, *sdisp, *edisp;
int y, len, incs, off, page;
scr = &vgascreen[0];
if(scr->gscreen == nil || scr->useflush == 0)
return;
if(scr->dev && scr->dev->flush){
scr->dev->flush(scr, r);
return;
}
if(rectclip(&r, scr->gscreen->r) == 0)
return;
disp = scr->vaddr;
incs = scr->gscreen->width*BY2WD;
off = (r.min.x*scr->gscreen->depth) / 8;
len = (r.max.x*scr->gscreen->depth + 7) / 8;
len -= off;
off += r.min.y*incs;
sp = scr->gscreendata->bdata + scr->gscreen->zero + off;
/*
* Linear framebuffer with softscreen.
*/
if(scr->paddr){
sdisp = disp+off;
for(y = r.min.y; y < r.max.y; y++) {
memmove(sdisp, sp, len);
sp += incs;
sdisp += incs;
}
return;
}
/*
* Paged framebuffer window.
*/
if(scr->dev == nil || scr->dev->page == nil)
return;
page = off/scr->apsize;
off %= scr->apsize;
sdisp = disp+off;
edisp = disp+scr->apsize;
scr->dev->page(scr, page);
for(y = r.min.y; y < r.max.y; y++) {
if(sdisp + incs < edisp) {
memmove(sdisp, sp, len);
sp += incs;
sdisp += incs;
}
else {
off = edisp - sdisp;
page++;
if(off <= len){
if(off > 0)
memmove(sdisp, sp, off);
scr->dev->page(scr, page);
if(len - off > 0)
memmove(disp, sp+off, len - off);
}
else {
memmove(sdisp, sp, len);
scr->dev->page(scr, page);
}
sp += incs;
sdisp += incs - scr->apsize;
}
}
}
void
getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb)
{
VGAscr *scr;
ulong x;
scr = &vgascreen[0];
if(scr->gscreen == nil)
return;
switch(scr->gscreen->depth){
default:
x = 0x0F;
break;
case 8:
x = 0xFF;
break;
}
p &= x;
lock(&cursor);
*pr = scr->colormap[p][0];
*pg = scr->colormap[p][1];
*pb = scr->colormap[p][2];
unlock(&cursor);
}
int
setpalette(ulong p, ulong r, ulong g, ulong b)
{
VGAscr *scr;
int d;
scr = &vgascreen[0];
d = scr->palettedepth;
lock(&cursor);
scr->colormap[p][0] = r;
scr->colormap[p][1] = g;
scr->colormap[p][2] = b;
vgao(PaddrW, p);
vgao(Pdata, r>>(32-d));
vgao(Pdata, g>>(32-d));
vgao(Pdata, b>>(32-d));
unlock(&cursor);
return ~0;
}
/*
* On some video cards (e.g. Mach64), the palette is used as the
* DAC registers for >8-bit modes. We don't want to set them when the user
* is trying to set a colormap and the card is in one of these modes.
*/
int
setcolor(ulong p, ulong r, ulong g, ulong b)
{
VGAscr *scr;
int x;
scr = &vgascreen[0];
if(scr->gscreen == nil)
return 0;
switch(scr->gscreen->depth){
case 1:
case 2:
case 4:
x = 0x0F;
break;
case 8:
x = 0xFF;
break;
default:
return 0;
}
p &= x;
return setpalette(p, r, g, b);
}
void
swenable(VGAscr*)
{
swcursorload(&arrow);
}
void
swdisable(VGAscr*)
{
}
void
swload(VGAscr*, Cursor *curs)
{
swcursorload(curs);
}
int
swmove(VGAscr*, Point p)
{
swcursorhide();
swcursordraw(p);
return 0;
}
VGAcur swcursor =
{
"soft",
swenable,
swdisable,
swload,
swmove,
};
void
cursoron(void)
{
VGAscr *scr;
VGAcur *cur;
scr = &vgascreen[0];
cur = scr->cur;
if(cur == nil || cur->move == nil)
return;
if(cur == &swcursor)
qlock(&drawlock);
lock(&cursor);
cur->move(scr, mousexy());
unlock(&cursor);
if(cur == &swcursor)
qunlock(&drawlock);
}
void
cursoroff(void)
{
}
void
setcursor(Cursor* curs)
{
VGAscr *scr;
scr = &vgascreen[0];
if(scr->cur == nil || scr->cur->load == nil)
return;
scr->cur->load(scr, curs);
}
int hwaccel = 1;
int hwblank = 0; /* turned on by drivers that are known good */
int panning = 0;
int
hwdraw(Memdrawparam *par)
{
VGAscr *scr;
Memimage *dst, *src, *mask;
Memdata *scrd;
int m;
scr = &vgascreen[0];
scrd = scr->gscreendata;
if(scr->gscreen == nil || scrd == nil)
return 0;
if((dst = par->dst) == nil || dst->data == nil)
return 0;
if((src = par->src) && src->data == nil)
src = nil;
if((mask = par->mask) && mask->data == nil)
mask = nil;
if(scr->cur == &swcursor){
if(dst->data->bdata == scrd->bdata)
swcursoravoid(par->r);
if(src && src->data->bdata == scrd->bdata)
swcursoravoid(par->sr);
if(mask && mask->data->bdata == scrd->bdata)
swcursoravoid(par->mr);
}
if(hwaccel == 0)
return 0;
if(dst->data->bdata != scrd->bdata || src == nil || mask == nil)
return 0;
if(scr->fill==nil && scr->scroll==nil)
return 0;
/*
* If we have an opaque mask and source is one opaque
* pixel we can convert to the destination format and just
* replicate with memset.
*/
m = Simplesrc|Simplemask|Fullmask;
if(scr->fill
&& (par->state&m)==m
&& ((par->srgba&0xFF) == 0xFF)
&& (par->op&S) == S)
return scr->fill(scr, par->r, par->sdval);
/*
* If no source alpha, an opaque mask, we can just copy the
* source onto the destination. If the channels are the same and
* the source is not replicated, memmove suffices.
*/
m = Simplemask|Fullmask;
if(scr->scroll
&& src->data->bdata==dst->data->bdata
&& !(src->flags&Falpha)
&& (par->state&m)==m
&& (par->op&S) == S)
return scr->scroll(scr, par->r, par->sr);
return 0;
}
void
blankscreen(int blank)
{
VGAscr *scr;
scr = &vgascreen[0];
if(hwblank){
if(scr->blank)
scr->blank(scr, blank);
else
vgablank(scr, blank);
}
}
void
vgalinearpci(VGAscr *scr)
{
ulong paddr;
int i, size, best;
Pcidev *p;
p = scr->pci;
if(p == nil)
return;
/*
* Scan for largest memory region on card.
* Some S3 cards (e.g. Savage) have enormous
* mmio regions (but even larger frame buffers).
* Some 3dfx cards (e.g., Voodoo3) have mmio
* buffers the same size as the frame buffer,
* but only the frame buffer is marked as
* prefetchable (bar&8). If a card doesn't fit
* into these heuristics, its driver will have to
* call vgalinearaddr directly.
*/
best = -1;
for(i=0; i<nelem(p->mem); i++){
if(p->mem[i].bar&1) /* not memory */
continue;
if(p->mem[i].size < 640*480) /* not big enough */
continue;
if(best==-1
|| p->mem[i].size > p->mem[best].size
|| (p->mem[i].size == p->mem[best].size
&& (p->mem[i].bar&8)
&& !(p->mem[best].bar&8)))
best = i;
}
if(best >= 0){
paddr = p->mem[best].bar & ~0x0F;
size = p->mem[best].size;
vgalinearaddr(scr, paddr, size);
return;
}
error("no video memory found on pci card");
}
void
vgalinearaddr(VGAscr *scr, ulong paddr, int size)
{
int x, nsize;
ulong npaddr;
/*
* new approach. instead of trying to resize this
* later, let's assume that we can just allocate the
* entire window to start with.
*/
if(scr->paddr == paddr && size <= scr->apsize)
return;
if(scr->paddr){
/*
* could call vunmap and vmap,
* but worried about dangling pointers in devdraw
*/
error("cannot grow vga frame buffer");
}
/* round to page boundary, just in case */
x = paddr&(BY2PG-1);
npaddr = paddr-x;
nsize = PGROUND(size+x);
/*
* Don't bother trying to map more than 4000x4000x32 = 64MB.
* We only have a 256MB window.
*/
if(nsize > 64*MB)
nsize = 64*MB;
scr->vaddr = vmap(npaddr, nsize);
if(scr->vaddr == 0)
error("cannot allocate vga frame buffer");
scr->vaddr = (char*)scr->vaddr+x;
scr->paddr = paddr;
scr->apsize = nsize;
/* let mtrr harmlessly fail on old CPUs, e.g., P54C */
if(!waserror()){
mtrr(npaddr, nsize, "wc");
poperror();
}
}