1 /*
   2  * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #import "MetalGraphicsConfig.h"
  27 #import "MetalLayer.h"
  28 #import "shaders/MetalShaderTypes.h"
  29 #import "ThreadUtilities.h"
  30 #import "LWCToolkit.h"
  31 #import "MetalSurfaceData.h"
  32 #import "VertexDataManager.h"
  33 
  34 
  35 //extern NSOpenGLPixelFormat *sharedPixelFormat;
  36 //extern NSOpenGLContext *sharedContext;
  37 
  38 @implementation MetalLayer
  39 
  40 @synthesize javaLayer;
  41 @synthesize mtlTexture;
  42 //@synthesize target;
  43 @synthesize textureWidth;
  44 @synthesize textureHeight;
  45 @synthesize mtlLibrary;
  46 @synthesize mtlRenderPipelineDescriptor;
  47 @synthesize renderPipelineState;
  48 //@synthesize LineVertexBuffer;
  49 //@synthesize QuadVertexBuffer;
  50 #ifdef REMOTELAYER
  51 //@synthesize parentLayer;
  52 //@synthesize remoteLayer;
  53 //@synthesize jrsRemoteLayer;
  54 #endif
  55 
  56 - (id) initWithJavaLayer:(JNFWeakJObjectWrapper *)layer;
  57 {
  58 AWT_ASSERT_APPKIT_THREAD;
  59     // Initialize ourselves
  60     self = [super init];
  61     if (self == nil) return self;
  62 
  63     self.javaLayer = layer;
  64 
  65     // NOTE: async=YES means that the layer is re-cached periodically
  66     //self.displaySyncEnabled = NO;
  67     self.contentsGravity = kCAGravityTopLeft;
  68     //Layer backed view
  69     //self.needsDisplayOnBoundsChange = YES;
  70     //self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
  71 
  72     //fprintf(stdout, "MetalLayer_initWithJavaLayer\n");fflush(stdout);
  73     //Disable CALayer's default animation
  74     NSMutableDictionary * actions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
  75                                     [NSNull null], @"anchorPoint",
  76                                     [NSNull null], @"bounds",
  77                                     [NSNull null], @"contents",
  78                                     [NSNull null], @"contentsScale",
  79                                     [NSNull null], @"onOrderIn",
  80                                     [NSNull null], @"onOrderOut",
  81                                     [NSNull null], @"position",
  82                                     [NSNull null], @"sublayers",
  83                                     nil];
  84     self.actions = actions;
  85     [actions release];
  86 
  87     //textureID = 0; // texture will be created by rendering pipe
  88     mtlTexture = NULL;
  89     //target = 0;
  90 
  91     return self;
  92 }
  93 
  94 - (void) dealloc {
  95     self.javaLayer = nil;
  96     [super dealloc];
  97 
  98     VertexDataManager_freeAllPrimitives();
  99 }
 100 
 101 /*
 102 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
 103     return CGLRetainPixelFormat(sharedPixelFormat.CGLPixelFormatObj);
 104 }
 105 
 106 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
 107     CGLContextObj contextObj = NULL;
 108     CGLCreateContext(pixelFormat, sharedContext.CGLContextObj, &contextObj);
 109     return contextObj;
 110 }*/
 111 
 112 // use texture (intermediate buffer) as src and blit it to the layer
 113 /*- (void) blitTexture
 114 {
 115     if (textureID == 0) {
 116         return;
 117     }
 118 
 119     glEnable(target);
 120     glBindTexture(target, textureID);
 121 
 122     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // srccopy
 123 
 124     float swid = 1.0f, shgt = 1.0f;
 125     if (target == GL_TEXTURE_RECTANGLE_ARB) {
 126         swid = textureWidth;
 127         shgt = textureHeight;
 128     }
 129     glBegin(GL_QUADS);
 130     glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f);
 131     glTexCoord2f(swid, 0.0f); glVertex2f( 1.0f, -1.0f);
 132     glTexCoord2f(swid, shgt); glVertex2f( 1.0f,  1.0f);
 133     glTexCoord2f(0.0f, shgt); glVertex2f(-1.0f,  1.0f);
 134     glEnd();
 135 
 136     glBindTexture(target, 0);
 137     glDisable(target);
 138 }*/
 139 
 140 /*-(BOOL)canDrawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp{
 141     return textureID == 0 ? NO : YES;
 142 }
 143 
 144 -(void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
 145 {
 146     AWT_ASSERT_APPKIT_THREAD;
 147 
 148     JNIEnv *env = [ThreadUtilities getJNIEnv];
 149     static JNF_CLASS_CACHE(jc_JavaLayer, "sun/java2d/opengl/CGLLayer");
 150     static JNF_MEMBER_CACHE(jm_drawInCGLContext, jc_JavaLayer, "drawInCGLContext", "()V");
 151 
 152     jobject javaLayerLocalRef = [self.javaLayer jObjectWithEnv:env];
 153     if ((*env)->IsSameObject(env, javaLayerLocalRef, NULL)) {
 154         return;
 155     }
 156 
 157     // Set the current context to the one given to us.
 158     CGLSetCurrentContext(glContext);
 159 
 160     // Should clear the whole CALayer, because it can be larger than our texture.
 161     glClearColor(0.0, 0.0, 0.0, 0.0);
 162     glClear(GL_COLOR_BUFFER_BIT);
 163 
 164     glViewport(0, 0, textureWidth, textureHeight);
 165 
 166     JNFCallVoidMethod(env, javaLayerLocalRef, jm_drawInCGLContext);
 167     (*env)->DeleteLocalRef(env, javaLayerLocalRef);
 168 
 169     // Call super to finalize the drawing. By default all it does is call glFlush().
 170     [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp];
 171 
 172     CGLSetCurrentContext(NULL);
 173 }*/
 174 
 175 @end
 176 
 177 
 178 
 179 static jlong cachedLayer = 0;
 180 static float drawColor[4] = {0.0, 0.0, 0.0, 0.0};
 181 /*
 182  * Class:     sun_java2d_metal_MetalLayer
 183  * Method:    nativeCreateLayer
 184  * Signature: ()J
 185  */
 186 JNIEXPORT jlong JNICALL
 187 Java_sun_java2d_metal_MetalLayer_nativeCreateLayer
 188 (JNIEnv *env, jobject obj)
 189 {
 190     __block MetalLayer *layer = nil;
 191 
 192     //fprintf(stdout, "MetalLayer_nativeCreateLayer\n");fflush(stdout);
 193 JNF_COCOA_ENTER(env);
 194 
 195     JNFWeakJObjectWrapper *javaLayer = [JNFWeakJObjectWrapper wrapperWithJObject:obj withEnv:env];
 196 
 197     [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
 198             AWT_ASSERT_APPKIT_THREAD;
 199         
 200             layer = [[MetalLayer alloc] initWithJavaLayer: javaLayer];
 201 
 202             //cachedLayer = ptr_to_jlong(layer);
 203     }];
 204     
 205 JNF_COCOA_EXIT(env);
 206 
 207     return ptr_to_jlong(layer);
 208 }
 209 
 210 
 211 
 212 JNIEXPORT jlong JNICALL
 213 Java_sun_java2d_metal_MetalLayer_nativeInitLayer
 214 (JNIEnv *env, jobject obj, jlong configInfo, jlong layer)
 215 {
 216 
 217 JNF_COCOA_ENTER(env);
 218     MetalGraphicsConfigInfo *pInfo =
 219         (MetalGraphicsConfigInfo *)jlong_to_ptr(configInfo);
 220     if ((pInfo == NULL)) {
 221         return -1;
 222     }
 223 
 224     MetalLayer *mtlLayer = jlong_to_ptr(layer);
 225 
 226     mtlLayer.device = pInfo->device;
 227     mtlLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
 228 
 229     //mtlLayer.commandQueue = pInfo->commandQueue;
 230 
 231     // ------------------------------------------------------------------------------------------------
 232     // TODO : Currently we manually compile and copy the shader library to /tmp.
 233     //        Need to complete build changes - to build it and read from some other location within jdk
 234     // ------------------------------------------------------------------------------------------------
 235     // Load shader library
 236     /*NSError *error = nil;
 237     mtlLayer.mtlLibrary = [mtlLayer.device newLibraryWithFile: @"/tmp/BaseShader.metallib" error:&error];
 238     if (!mtlLayer.mtlLibrary) {
 239         NSLog(@"Failed to load library. error %@", error);
 240         //exit(0);
 241     }*/
 242 
 243     NSError* error = nil;
 244     NSString* content = @"#include <metal_stdlib>\n"
 245     "#include <simd/simd.h>\n"
 246     "using namespace metal;\n"
 247     "struct MetalVertex"
 248     "{"
 249     "vector_float4 position;"
 250     "vector_float4 color;"
 251     "};\n"
 252     "struct VertexOut {"
 253     "float4 color;"
 254     "float4 pos [[position]];"
 255     "};\n"
 256     "vertex VertexOut vertexShader(device MetalVertex *vertices [[buffer(0)]],"
 257                               "constant unsigned int *viewportSize [[buffer(1)]],"
 258                               "uint vid [[vertex_id]]) {\n"
 259     "VertexOut out;"
 260     "out.pos = vertices[vid].position;"
 261     "\n"
 262     "float halfViewWidth = (float)(viewportSize[0] >> 1);"
 263     "float halfViewHeight = (float)(viewportSize[1] >> 1);"
 264     "\n"
 265     "out.pos.x = (out.pos.x - halfViewWidth) / halfViewWidth;"
 266     "out.pos.y = (halfViewHeight - out.pos.y) / halfViewHeight;"
 267     "\n"
 268     "out.color = vertices[vid].color;"
 269     "\n"
 270     "return out;"
 271     "}\n"
 272     "fragment float4 fragmentShader(MetalVertex in [[stage_in]]) {"
 273     "return in.color;"
 274     "}";
 275 
 276     mtlLayer.mtlLibrary = [mtlLayer.device newLibraryWithSource:content options:nil error:&error];
 277     if (!mtlLayer.mtlLibrary) {
 278         NSLog(@"Failed to create shader library from source. error %@", error);
 279         //exit(0);
 280     }
 281 
 282     //create a vertex and fragment objects
 283     id<MTLFunction> vertexProgram = [mtlLayer.mtlLibrary newFunctionWithName:@"vertexShader"];
 284     id<MTLFunction> fragmentProgram = [mtlLayer.mtlLibrary newFunctionWithName:@"fragmentShader"];
 285 
 286     
 287     mtlLayer.mtlRenderPipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
 288     
 289     [mtlLayer.mtlRenderPipelineDescriptor setVertexFunction:vertexProgram];
 290     [mtlLayer.mtlRenderPipelineDescriptor setFragmentFunction:fragmentProgram];
 291     
 292     //specify the target-texture pixel format
 293     mtlLayer.mtlRenderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
 294     
 295     //create the Rendering Pipeline Object
 296     mtlLayer.renderPipelineState = [mtlLayer.device newRenderPipelineStateWithDescriptor:mtlLayer.mtlRenderPipelineDescriptor error:nil];
 297 
 298     VertexDataManager_init(mtlLayer.device);
 299   
 300 
 301 JNF_COCOA_EXIT(env);
 302 
 303     return ptr_to_jlong(layer);
 304 }
 305 
 306 
 307 
 308 // Must be called under the RQ lock.
 309 /*JNIEXPORT void JNICALL
 310 Java_sun_java2d_metal_MetalLayer_nativeValidate
 311 (JNIEnv *env, jclass cls, jlong layer, jlong view)
 312 {
 313 
 314 JNF_COCOA_ENTER(env);
 315 
 316     MetalLayer *mtlLayer = jlong_to_ptr(layer);
 317     NSView *nsView = jlong_to_ptr(view);
 318 
 319     mtlLayer.frame = nsView.bounds;
 320     [mtlLayer setDrawableSize: nsView.bounds.size];
 321 
 322     mtlLayer.textureWidth = nsView.bounds.size.width;
 323     mtlLayer.textureHeight = nsView.bounds.size.height;
 324 
 325     NSLog(@"Validate : Width : %f", nsView.bounds.size.width);
 326     NSLog(@"Validate : Height : %f", nsView.bounds.size.height);
 327 
 328 JNF_COCOA_EXIT(env);
 329 }*/
 330 
 331 // Must be called under the RQ lock.
 332 JNIEXPORT void JNICALL
 333 Java_sun_java2d_metal_MetalLayer_validate
 334 (JNIEnv *env, jclass cls, jlong layerPtr, jobject surfaceData)
 335 {
 336     MetalLayer *layer = OBJC(layerPtr);
 337     //fprintf(stdout, "MetalLayer_validate\n");fflush(stdout);
 338     if (surfaceData != NULL) {
 339         MetalSDOps *metalsdo = (MetalSDOps*) SurfaceData_GetOps(env, surfaceData);
 340         // TODO : Check whether we have to use pointer or instance variable
 341         //fprintf(stdout, "MetalLayer_validate replace mtlTexture\n");fflush(stdout);
 342         layer.mtlTexture = metalsdo->mtlTexture;
 343         //layer.target = GL_TEXTURE_2D;
 344         layer.textureWidth = metalsdo->width;
 345         layer.textureHeight = metalsdo->height;
 346 
 347         VertexDataManager_reset(metalsdo->configInfo->device);
 348 
 349         NSLog(@"Validate : Width : %f", layer.textureWidth);
 350         NSLog(@"Validate : height : %f", layer.textureHeight);
 351 
 352     } else {
 353         //fprintf(stdout, "MetalLayer_validate Null SD \n");fflush(stdout);
 354         //layer.textureID = 0;
 355     }
 356 }
 357 
 358 
 359 
 360 /*
 361 // Must be called on the AppKit thread and under the RQ lock.
 362 JNIEXPORT void JNICALL
 363 Java_sun_java2d_opengl_CGLLayer_blitTexture
 364 (JNIEnv *env, jclass cls, jlong layerPtr)
 365 {
 366     CGLLayer *layer = jlong_to_ptr(layerPtr);
 367 
 368     [layer blitTexture];
 369 }*/
 370 
 371 JNIEXPORT void JNICALL
 372 Java_sun_java2d_metal_MetalLayer_nativeSetScale
 373 (JNIEnv *env, jclass cls, jlong layerPtr, jdouble scale)
 374 {
 375     JNF_COCOA_ENTER(env);
 376     MetalLayer *layer = jlong_to_ptr(layerPtr);
 377     // We always call all setXX methods asynchronously, exception is only in 
 378     // this method where we need to change native texture size and layer's scale
 379     // in one call on appkit, otherwise we'll get window's contents blinking, 
 380     // during screen-2-screen moving.
 381     [ThreadUtilities performOnMainThreadWaiting:[NSThread isMainThread] block:^(){
 382         layer.contentsScale = scale;
 383     }];
 384     JNF_COCOA_EXIT(env);
 385 }