Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discussion] Can SDLV1 handle BW 1 bpp source frame #35

Open
rickyzhang82 opened this issue Sep 25, 2017 · 8 comments
Open

[Discussion] Can SDLV1 handle BW 1 bpp source frame #35

rickyzhang82 opened this issue Sep 25, 2017 · 8 comments

Comments

@rickyzhang82
Copy link

rickyzhang82 commented Sep 25, 2017

This is NOT an issue in your SDL2. But I can't find anyone who can discuss this topic with. Sorry to abuse your issue board.

Recently, I'm working on enabling 24 bit ROM in BII. Someone fixed broken frame buffer memory access. I fixed emulated hard drive. Now I want to make it work under SDL.

I traced the frame buffer drawing in the host down to the function in below.

Before I asked you questions, here is some background

  1. Screen size is 512x342
  2. Black and White -- 1 bit per pixel
  3. For Macintosh with 24bit ROM, it has 4MB memory limit. Buffer memory patch make it stays in the last page of memory, which is not hard coded address a0000000.
  4. The patch make drawing works in X11 but not SDL.

Here are my questions regarding to the function below:
1. I understood that it tries to find the minimum rectangle that contain different pixels between the current the_buffer and the previous the_buffer_copy, but what does variable wide and hide mean?
1. I have forced SDL use 1 bit per pixel, but the Destination bytes per row are still 512, which is one byte per pixel under 512 x 342.

-- I got the answers.

// Static display update (fixed frame rate, but incremental)
1842 static void update_display_static(driver_base *drv)
1843 {
1844     // Incremental update code
1845     int wide = 0, high = 0;
1846     uint32 x1, x2, y1, y2;
1847     const VIDEO_MODE &mode = drv->mode;
1848     int bytes_per_row = VIDEO_MODE_ROW_BYTES;
1849     uint8 *p, *p2;
1850 
1851     // Check for first line from top and first line from bottom that have changed
1852     y1 = 0;
1853     for (uint32 j = 0; j < VIDEO_MODE_Y; j++) {
1854         if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1855             y1 = j;
1856             break;
1857         }
1858     }
1859     y2 = y1 - 1;
1860     for (uint32 j = VIDEO_MODE_Y; j-- > y1; ) {
1861         if (memcmp(&the_buffer[j * bytes_per_row], &the_buffer_copy[j * bytes_per_row], bytes_per_row)) {
1862             y2 = j;
1863             break;
1864         }
1865     }
1866     high = y2 - y1 + 1;
1867 
1868     // Check for first column from left and first column from right that have changed
1869     if (high) {
1870         if (VIDEO_MODE_DEPTH < VIDEO_DEPTH_8BIT) {
1871             const int src_bytes_per_row = bytes_per_row;
1872             const int dst_bytes_per_row = drv->s->pitch;
1873             const int pixels_per_byte = VIDEO_MODE_X / src_bytes_per_row;
1874 
1875             x1 = VIDEO_MODE_X / pixels_per_byte;
1876             for (uint32 j = y1; j <= y2; j++) {
1877                 p = &the_buffer[j * bytes_per_row];
1878                 p2 = &the_buffer_copy[j * bytes_per_row];
1879                 for (uint32 i = 0; i < x1; i++) {
1880                     if (*p != *p2) {
1881                         x1 = i;
1882                         break;
1883                     }
1884                     p++; p2++;
1885                 }
1886             }
1887             x2 = x1;
1888             for (uint32 j = y1; j <= y2; j++) {
1889                 p = &the_buffer[j * bytes_per_row];
1890                 p2 = &the_buffer_copy[j * bytes_per_row];
1891                 p += bytes_per_row;
1892                 p2 += bytes_per_row;
1893                 for (uint32 i = (VIDEO_MODE_X / pixels_per_byte); i > x2; i--) {
1894                     p--; p2--;
1895                     if (*p != *p2) {
1896                         x2 = i;
1897                         break;
1898                     }
1899                 }
1900             }
1901             x1 *= pixels_per_byte;
1902             x2 *= pixels_per_byte;
1903             wide = (x2 - x1 + pixels_per_byte - 1) & -pixels_per_byte;
1904 
1905             // Update copy of the_buffer
1906             if (high && wide) {
1907 
1908                 // Lock surface, if required
1909                 if (SDL_MUSTLOCK(drv->s))
1910                     SDL_LockSurface(drv->s);
1911 
1912                 // Blit to screen surface
1913                 int si = y1 * src_bytes_per_row + (x1 / pixels_per_byte);
1914                 int di = y1 * dst_bytes_per_row + x1;
1915                 for (uint32 j = y1; j <= y2; j++) {
1916                     memcpy(the_buffer_copy + si, the_buffer + si, wide / pixels_per_byte);
1917                     Screen_blit((uint8 *)drv->s->pixels + di, the_buffer + si, wide / pixels_per_byte);
1918                     si += src_bytes_per_row;
1919                     di += dst_bytes_per_row;
1920                 }
1921 
1922                 // Unlock surface, if required
1923                 if (SDL_MUSTLOCK(drv->s))
1924                     SDL_UnlockSurface(drv->s);
1925 
1926                 // Refresh display
1927                 SDL_UpdateRect(drv->s, x1, y1, wide, high);
1928             }
1929 
1930         } else {
1931             const int bytes_per_pixel = VIDEO_MODE_ROW_BYTES / VIDEO_MODE_X;
1932             const int dst_bytes_per_row = drv->s->pitch;
1933 
1934             x1 = VIDEO_MODE_X;
1935             for (uint32 j = y1; j <= y2; j++) {
1936                 p = &the_buffer[j * bytes_per_row];
1937                 p2 = &the_buffer_copy[j * bytes_per_row];
1938                 for (uint32 i = 0; i < x1 * bytes_per_pixel; i++) {
1939                     if (*p != *p2) {
1940                         x1 = i / bytes_per_pixel;
1941                         break;
1942                     }
1943                     p++; p2++;
1944                 }
1945             }
1946             x2 = x1;
1947             for (uint32 j = y1; j <= y2; j++) {
1948                 p = &the_buffer[j * bytes_per_row];
1949                 p2 = &the_buffer_copy[j * bytes_per_row];
1950                 p += bytes_per_row;
1951                 p2 += bytes_per_row;
1952                 for (uint32 i = VIDEO_MODE_X * bytes_per_pixel; i > x2 * bytes_per_pixel; i--) {
1953                     p--;
1954                     p2--;
1955                     if (*p != *p2) {
1956                         x2 = i / bytes_per_pixel;
1957                         break;
1958                     }
1959                 }
1960             }
1961             wide = x2 - x1;
1962 
1963             // Update copy of the_buffer
1964             if (high && wide) {
1965 
1966                 // Lock surface, if required
1967                 if (SDL_MUSTLOCK(drv->s))
1968                     SDL_LockSurface(drv->s);
1969 
1970                 // Blit to screen surface
1971                 for (uint32 j = y1; j <= y2; j++) {
1972                     uint32 i = j * bytes_per_row + x1 * bytes_per_pixel;
1973                     int dst_i = j * dst_bytes_per_row + x1 * bytes_per_pixel;
1974                     memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * wide);
1975                     Screen_blit((uint8 *)drv->s->pixels + dst_i, the_buffer + i, bytes_per_pixel * wide);
1976                 }
1977 
1978                 // Unlock surface, if required
1979                 if (SDL_MUSTLOCK(drv->s))
1980                     SDL_UnlockSurface(drv->s);
1981 
1982                 // Refresh display
1983                 SDL_UpdateRect(drv->s, x1, y1, wide, high);
1984             }
1985         }
1986     }
1987 }
1988 
@rickyzhang82
Copy link
Author

I found the culprit that cause SDL failed in src/CrossPlatform/video_blit.cpp:

0580     // The UAE memory handlers will blit correctly
0581     // --> no need for specialised blitters here
0582     Screen_blit = Blit_Copy_Raw;

In SDL, the destination is one byte per pixel while emulated Mac is one bit per pixel. However, it didn't handle this case for SDL.

@rickyzhang82
Copy link
Author

Well, I sort of fixed 1 bit per pixel issue in SDL.

When expand one bit to one byte for SDL surface, I added line 307.

I don't quite follow color mask and palette in video_sdl.

I wonder if SDL2 supports one bit per pixel?

304 /* -------------------------------------------------------------------------- */
305 /* --- 1/2/4-bit indexed to 8-bit mode conversion                         --- */
306 /* -------------------------------------------------------------------------- */
307 #define CONVERT_BW(byte) (byte)==1?0:255
308 static void Blit_Expand_1_To_8(uint8 * dest, const uint8 * p, uint32 length)
309 {
310     uint8 *q = (uint8 *)dest;
311     for (uint32 i=0; i<length; i++) {
312         uint8 c = *p++;
313         *q++ = CONVERT_BW(c >> 7);
314         *q++ = CONVERT_BW((c >> 6) & 1);
315         *q++ = CONVERT_BW((c >> 5) & 1);
316         *q++ = CONVERT_BW((c >> 4) & 1);
317         *q++ = CONVERT_BW((c >> 3) & 1);
318         *q++ = CONVERT_BW((c >> 2) & 1);
319         *q++ = CONVERT_BW((c >> 1) & 1);
320         *q++ = CONVERT_BW(c & 1);
321     }
322 }

@DavidLudwig
Copy link
Owner

Offhand, I vaguely recall SDL2 having code to work with 1-bit graphics. I know that I have tested black and white color modes in the SDL2 backend, and I think Basilisk was assuming that a 1-bit buffer was around somewhere, but I'm not 100% certain of this offhand.

Regarding the SDL2 backend, it deals with things a bit differently than with the SDL1 backend. In particular, it leaves much of the color conversion work to SDL2. I set things up to assume that there is, at minimum, always a 32-bit ARGB buffer somewhere in memory. If the guest OS needs to work with something other than 32-bit ARGB, the SDL2 backend will create a 2nd buffer, in the format that the guest OS wants. As screen-updates are detected, the SDL2 backend will convert that portion of the buffer, from the guest OS' format, to 32-bit ARGB (which, from there, will typically get uploaded to a GPU texture, via SDL_Texture APIs).

With regards to the SDL1 backend, I vaguely recall it trying to get the host OS to give it a buffer in a compatible format, but I am hazy on the details.

If you could use a hand with further work on this, or have any other questions regarding the SDL backend(s), let me know.

@DavidLudwig
Copy link
Owner

To add:

I do recall Basilisk doing some work with regards to color conversion, and I recall re-enabling some of it, when getting certain guest-OS color modes working. It is, admittedly, an area of code I could probably stand to learn more about.

@rickyzhang82
Copy link
Author

rickyzhang82 commented Sep 26, 2017

I skimmed through SDL v1 and BII emulation in video driver for 24 bit ROM bank addressing. Here is my understanding:

  1. Host OS allocates the_buffer and the_buffer_copy. It uses memcpy to copy updated frame buffer from the_buffer to the previous one the_buffer_copy so that it can compute the smallest rectangle of updated frame buffer.
  2. Guest OS maps frame buffer to the_buffer. MacFrameBaseMac is hard coded at a000000, which is not the correct address for 24 bit ROM. But incorrect MacFrameBaseMac is not a deal breaker in 24 bit ROM. Because it is not a truly emulated NuBus video driver. The variable has never been used.
  3. 24 bit ROM only supports Black and White, which is one bit per pixel in guest OS, while SDL V1 and SDL v2 only allows uses at least one byte per pixel. Thus, it has to use BII function Blit_Expand_1_To_8 to expand one bit to 8 bits for SDL.

IMO, unless you have a guest OS frame buffer like I recently enabled in 24 Bit ROM in System 6, you may not get into this case from your test.

I will also share my Python script and method to grab frame buffer from your machine:

  1. Enter cxmon. To get access to frame in memory, Global variable ScrnBase at 0x824, 512 x 342 x 1bit / 8 bits per byte= 21888 =0x5580. You can also change the size based on your screen mode.
[0000000000000000]-> m 824
0000000000000824: 003fa700 00000005 00000005 000f000f  '.?..............'
[0000000000000924]-> ] 003fa700 5580 "24bit-sdl.bitmap"
00005580 bytes written from 00000000003fa700 to 00000000003ffc7f
  1. Show image by OpenCV.
import cv2
import numpy as np
# 512x342
np_array = np.fromfile('/Users/Ricky/repo/github/sf-macemu-sdl/BasiliskII/src/Unix/24bit-sdl.bitmap', np.uint8)
bit_stream = np.unpackbits(np_array).reshape(342, 512)
bit_stream *= 255;
cv2.imshow('image', bit_stream)
cv2.waitKey(0)
cv2.destroyAllWindows()

Computer graphics is not my expertise, I'd wonder if you could share your insight of SDL palette in BII. More specifically, how that R,G,B mask work.

Thanks in advance!

@DavidLudwig
Copy link
Owner

Here is a high-level overview of SDL palettes:

  1. pixel buffers in SDL are typically represented via an instance of struct SDL_Surface
  2. SDL_Surface instances include a pointer to an instance of struct SDL_PixelFormat, which describes a specific, pixel format (such as ARGB8888, RGB565, etc.)
  3. SDL_PixelFormat instances can, optionally, have a palette associated with them. These palettes are represented by instances of struct SDL_Palette, which include an array of colors.
  4. palette-ized surfaces (in SDL) are represented by an SDL_Surface, whose SDL_PixelFormat has an SDL_Palette attached to it
  5. palettes are used, typically, when blitting one surface to another surface (via SDL_BlitSurface(), aka. SDL_UpperBlit())

Does this make sense?

@rickyzhang82
Copy link
Author

rickyzhang82 commented Oct 4, 2017

Correct me if I'm wrong.

  1. There is only one SDL surface in BII. So why do you mention blitting in SDL?
  2. In my case, is it appropriate to map the color from source frame buffer (guest OS) to target frame (SDL host OS) by manipulating the SDL palette? I found that SDL surface support 8 bit as its minimum bit per pixel. If I understand their doc correctly, the 8 bit pixel is the index of color array defined in SDL_Palette. If I could change the color array, I don't need to convert color in expanding one bit to 8 bits (rickyzhang82@ef9e53c)

@DavidLudwig
Copy link
Owner

  1. The SDL2 backend can have up to two, different, SDL surfaces: one in the format of the guest OS, one in the format of the host OS. If the two surfaces' pixel format will be the same, then the SDL2 backend will only create one surface, and share it between two pointers ('guest_surface' and 'host_surface'). If and when those two surfaces are different, then the SDL2 backend will perform blits from guest_surface to host_surface (and converting between the two pixel formats).

  2. Maybe? I am unsure if SDL can, directly, handle conversion from 1-bit to 8-bit palletized color. If you could use help in finding this out, let me know. I have some knowledge of SDL2's internals, but I don't know everything about it (and could probably stand to learn a bit more)!

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

No branches or pull requests

2 participants