Skip to content

Instantly share code, notes, and snippets.

@alyssarosenzweig
Created September 25, 2021 02:09
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alyssarosenzweig/7d8099cdb227d2de0a9e83b7de34c7f8 to your computer and use it in GitHub Desktop.
Save alyssarosenzweig/7d8099cdb227d2de0a9e83b7de34c7f8 to your computer and use it in GitHub Desktop.
AppleJPEGDriver decode routine for the Apple M1
/*
* Copyright (C) 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <assert.h>
#include <mach/mach.h>
#include <IOKit/IOKitLib.h>
#include <IOSurface/IOSurface.h>
#import <Cocoa/Cocoa.h>
@class NSNumber;
struct JPEGDriverArgs {
IOSurfaceID src_surface /*in*/;
unsigned int input_jpeg_file_size /*in*/; // GOOD
IOSurfaceID dest_surface /*in*/;
unsigned int output_buffer_size /*in*/; // GOOD
int unk10;
int pixel_x;
int pixel_y;
int unk1C;
int unk20;
int x_offset;
int y_offset;
int subsampling_mode;
int unk30;
int unk34;
int unk38;
int unk3C;
int unk40;
int unk44;
int unk48;
unsigned int decode_width;
unsigned int decode_height;
int unk5C;
} __attribute__((packed));
int main(int argc, char **argv)
{
(void) argc;
(void) argv;
kern_return_t ret;
CFDictionaryRef matching = IOServiceMatching("AppleJPEGDriver");
io_service_t service =
IOServiceGetMatchingService(kIOMasterPortDefault, matching);
if (!service) {
fprintf(stderr, "JPEG driver accelerator not found\n");
return 1;
}
io_connect_t connection = 0;
ret = IOServiceOpen(service, mach_task_self(), 0, &connection);
if (ret) {
fprintf(stderr, "Error from IOServiceOpen: %u\n", ret);
return 1;
}
IOSurfaceRef surfs[2];
char pixelFormat[4] = {'A', 'R', 'G', 'B'};
unsigned int p = 111;
memcpy(&p, &pixelFormat[0], 4);
printf("%X\n", p);
/* input jpeg */
surfs[0] = IOSurfaceCreate((CFDictionaryRef)@{(id)kIOSurfaceWidth: @120,
(id)kIOSurfaceHeight: @80,
(id)kIOSurfacePixelFormat: @(p),
(id)kIOSurfaceBytesPerElement: @4});
/* output image */
surfs[1] = IOSurfaceCreate((CFDictionaryRef)@{(id)kIOSurfaceWidth: @120,
(id)kIOSurfaceHeight: @80,
(id)kIOSurfaceBytesPerRow: @(120*4),
(id)kIOSurfacePixelFormat: @(p),
(id)kIOSurfaceBytesPerElement: @4});
void *base = IOSurfaceGetBaseAddress(surfs[0]);
FILE *fp = fopen("dump.jpg", "rb");
fread(base, 1, 3178, fp);
fclose(fp);
printf("Connection %d\n", connection);
size_t sz = sizeof(struct JPEGDriverArgs);
struct JPEGDriverArgs args = {
.src_surface = IOSurfaceGetID(surfs[0]),
.input_jpeg_file_size = 3178,
.dest_surface = IOSurfaceGetID(surfs[1]),
.decode_width = 120,
.decode_height = 80,
.pixel_x = 120,
.pixel_y = 80,
.output_buffer_size = 1024*1024*16,
.x_offset = 0,
.y_offset = 0,
};
ret = IOConnectCallStructMethod(connection, 1, &args,
sizeof(args), &args, &sz);
sleep(2);
ret = IOServiceClose(connection);
{
base = IOSurfaceGetBaseAddress(surfs[1]);
FILE *fp = fopen("dump.bin", "wb");
fwrite(base, 1, 120*80*4, fp);
fclose(fp);
}
if (ret) {
fprintf(stderr, "Error from IOServiceClose: %u\n", ret);
return 1;
}
}
@alyssarosenzweig
Copy link
Author

alyssarosenzweig commented Sep 25, 2021

You should be able to pass a four-character code 'abcd' to the pixel format parameter directly; Apple uses them all over the place. See for instance CoreVideo/CVPixelBuffer.h.
The only weird thing is that your code effectively sets p to 'BGRA' (or kCVPixelFormatType_32BGRA), not ARGB. So… which pixel format actually ends up being used?

BGRA, which is what I intended. Even though the endianness makes it looks like ARGB (which is a very odd format indeed..) I am just so used to reading fourcc's backwards at this point :p

@JoelLinn
Copy link

JoelLinn commented Sep 25, 2021

BGRA, which is what I intended. Even though the endianness makes it looks like ARGB (which is a very odd format indeed..) I am just so used to reading fourcc's backwards at this point :p

The world would be a better place with big endian systems only 🙃.
I sometimes run software tests on powerpc BE via the free unicamp minicloud, just to probe for funky memory indexing and char/numeric type casting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment